﻿'// ========================================================================= '
'// ------------------------------------------------------------------------- '
'//!				\file		CM_PolyArea.vb
'			        \date		june 2010
'			        \author		TNick
'
'			        \brief		
'
'
'   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'   This file is considered universal knowledge. No restrictions
'   are imposed; use as you see fit.
'   Please read ReadMe.txt and Licence.txt in root folder
'   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'
'   
'
'// ------------------------------------------------------------------------- '
'// ========================================================================= '
'//
'//
'//
'//
'//  IMPORTS     ------------------------------------------------------------ '
#Region "Imports"

Imports System
Imports Microsoft.Win32

Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.Customization
'Imports Autodesk.AutoCAD.Colors
'Imports Autodesk.AutoCAD.GraphicsSystem

#End Region
'//  IMPORTS     ============================================================ '
'//
'//
'//
'//
'//  DEFINITIONS    --------------------------------------------------------- '
<Assembly: CommandClass(GetType(CM_PolyArea))> 


#Region "Public Structures"
    

    '// --------------------------------------------------------------------- '
    '// defines the settings for polyline area
    Public Structure PAREASTG
        Dim iIterMax As Integer ' maximum number of iterations
        Dim bAskDir As Boolean ' ask directions after each step
        Dim dLastInA As Double ' last entered area
        Dim PrevDir As FSK_LINE
        Dim bHavePrevDir As Boolean

        '// leftover should be used to create polyline
        Dim bLftOvr As Boolean
        '// delete original polyline
        Dim bDelInitPoly As Boolean

        '// text entered in list area
        Dim sTxLstA  As String
    End Structure
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
    '// informations about a area that needs to be generated
    Public Structure PAREA
        '// This is the string that is to be placed in the middle
        '// If nothing or empty, there will be no string
        Dim sName As String
        '// the area that needs to be generated
        Dim dArea As Double
    End Structure
    '// ===================================================================== '

#End Region


'//  DEFINITIONS    ========================================================= '
'//
'//
'//
'//
'//  DATA    ---------------------------------------------------------------- '


'//  DATA    ================================================================ '
'//
'//
'//
'//
'//  CLASS    --------------------------------------------------------------- '
Public Class CM_PolyArea
	Inherits CModule


    '// --------------------------------------------------------------------- '
    '// DATA

    #Region "Public Constants"
        '// name as displayed in various places
	    Public Const sAPPNAME As String = "Polyline area"
        '// version as displayed in various places
	    Public Const sAPPVER As String = "1.0.0.0"
        '// short description
	    Public sAPPTIP As String

        '// Command. Brings up the settings dialog
        Public Const sCMD_SETTINGS As String = "TNIK_PA_STG"
        '// Command. Divide a polyline by direction
        Public Const sCMD_DIR As String = "TNIK_PA_DIR"
        '// Command. Divide a polyline by a point (Find a direction)
        Public Const sCMD_ROT As String = "TNIK_PA_ROT"
        '// Command. Create a new polyline by offseting existing one
        Public Const sCMD_OFFS As String = "TNIK_PA_OFFS"
        '// Command. Get to an area by moving a point on a direction
        Public Const sCMD_PT As String = "TNIK_PA_PT"
        '// Command. Similar to offset method, but only a range of points
        '// are moved
        Public Const sCMD_OFFRNG As String = "TNIK_PA_ORNG"
        '// Command. Insert a point in a polyline
        Public Const sCMD_INSPT As String = "TNIK_PA_INSPT"

    #End Region

    '// this is where the settings are stored durring run-time
    Public Shared Stgs As PAREASTG
    '// Current list of areas to generate
    Public Shared lArea() As PAREA = Nothing
    '// ===================================================================== '


#Region "Private general purpose Functions"


    '// --------------------------------------------------------------------- '
	'// This function saves current settings
	Private Shared Sub SaveSett()
		CSett.SaveStg(sAPPNAME, "Stgs.iIterMax", Stgs.iIterMax)
		CSett.SaveStg(sAPPNAME, "Stgs.bAskDir", Stgs.bAskDir)
		CSett.SaveStg(sAPPNAME, "Stgs.bHavePrevDir", Stgs.bHavePrevDir)
        If (Stgs.bHavePrevDir) Then
            CSett.GetDblStg(sAPPNAME, "Stgs.PrevDir.E_1", Stgs.PrevDir.E_1)
            CSett.GetDblStg(sAPPNAME, "Stgs.PrevDir.E_2", Stgs.PrevDir.E_2)
            CSett.GetDblStg(sAPPNAME, "Stgs.PrevDir.N_1", Stgs.PrevDir.N_1)
            CSett.GetDblStg(sAPPNAME, "Stgs.PrevDir.N_2", Stgs.PrevDir.N_2)
        End If
		CSett.SaveStg(sAPPNAME, "Stgs.bLftOvr", Stgs.bLftOvr)
		CSett.SaveStg(sAPPNAME, "Stgs.bDelInitPoly", Stgs.bDelInitPoly)
		CSett.SaveStg(sAPPNAME, "Stgs.sTxLstA", Stgs.sTxLstA)

	End Sub
    '// ===================================================================== '


    '// --------------------------------------------------------------------- '
	'// This function loads current settings
	Private Shared Sub GetSett()
		Stgs.iIterMax = CSett.GetIntStg(sAPPNAME, "Stgs.iIterMax", 100)
		Stgs.bAskDir = CSett.GetBolStg(sAPPNAME, "Stgs.bAskDir", True)

		Stgs.dLastInA = CSett.GetDblStg(sAPPNAME, "Stgs.dLastInA", 0)
		Stgs.bHavePrevDir = CSett.GetBolStg(sAPPNAME, "Stgs.bHavePrevDir", False)
        If (Stgs.bHavePrevDir) Then
            Stgs.PrevDir.E_1 = CSett.GetDblStg(sAPPNAME, "Stgs.PrevDir.E_1", 0)
            Stgs.PrevDir.E_2 = CSett.GetDblStg(sAPPNAME, "Stgs.PrevDir.E_2", 1)
            Stgs.PrevDir.N_1 = CSett.GetDblStg(sAPPNAME, "Stgs.PrevDir.N_1", 0)
            Stgs.PrevDir.N_2 = CSett.GetDblStg(sAPPNAME, "Stgs.PrevDir.N_2", 1)

            Dim dDst As Double = (Stgs.PrevDir.E_2 - Stgs.PrevDir.E_1)
            If (Math.Abs(dDst) < CMain.MARGIN) Then
                Stgs.PrevDir.bVert = True
                Stgs.PrevDir.m = 0
                Stgs.PrevDir.b = 0
            Else
                Stgs.PrevDir.bVert = False
                Stgs.PrevDir.m = ((Stgs.PrevDir.N_2 - Stgs.PrevDir.N_1) / dDst)
                Stgs.PrevDir.b = Stgs.PrevDir.N_1 - Stgs.PrevDir.m * Stgs.PrevDir.E_1
            End If
        End If

        Stgs.bLftOvr = CSett.GetBolStg(sAPPNAME, "Stgs.bLftOvr", True)
        Stgs.bDelInitPoly = CSett.GetBolStg(sAPPNAME, "Stgs.bDelInitPoly", False)

        Stgs.sTxLstA = CSett.GetStrStg(sAPPNAME, "Stgs.sTxLstA", "")

	End Sub
    '// ===================================================================== '


    '// --------------------------------------------------------------------- '
	'// This function shows current settings
	Private Shared Function PrintStg(ByRef lcStg As PAREASTG) As String
        '// TODO
		Return ( _
		 vbCrLf & CMain._T(TX.CSTG) & sAPPNAME & ": " & vbCrLf & _
		  CMain._T(TX.MAXITER) & " = " & lcStg.iIterMax.ToString() & vbCrLf & _
		  CMain._T(TX.PA_ASKDIR) & " = " & lcStg.bAskDir.ToString() & vbCrLf & _
		  CMain._T(TX.CREATEPLYLFT) & " = " & lcStg.bLftOvr.ToString() & vbCrLf & _
		  CMain._T(TX.DELORIGPOLY) & " = " & lcStg.bDelInitPoly.ToString() & vbCrLf) 

      Return Nothing
	End Function
    '// ===================================================================== '
    

#End Region


#Region "Common interface" ' -----------------------

    '// --------------------------------------------------------------------- '
	'// a new instance is created
	Public Sub New()
		Try
            sAPPTIP = CMain._T(TX.PAREA_INFO)
			Return
		Catch ex As System.Exception
			CMain.DebugException(ex)
			Return
		End Try
	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// the module is asked to terminate
	Public Overrides Function Terminate() As Boolean
		Try
			SaveSett()
			Return True
		Catch ex As System.Exception
			CMain.DebugException(ex)
			Return False
		End Try
	End Function
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// the module is asked to tell it's name
	Public Shared Function GetName() As String
		Return sAPPNAME
	End Function
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// the module is asked to tell about itself
	Public Shared Sub GetInfos(ByRef sName As String, ByRef sVer As String, ByRef sTip As String)
		sName = sAPPNAME
		sVer = sAPPVER
		sTip = CMain._T(TX.PAREA_INFO)
	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// the module is asked to tell about itself
	Public Shared Function GetDescription() As String
        Dim sOut As String = sAPPNAME
        Try
            Dim cIni As New IniWork(CApp.sAppPath & CMain.APPSTG)
            If cIni.HasErrors = True Then Exit Try
            sOut = cIni.Read(sAPPNAME, CMain.DESCRKEY & _
                             CMain.eAppLang.ToString,sAPPNAME)
        Catch ex As System.Exception
            CMain.DebugException(ex)
        End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
        Return sOut
	End Function
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// the module is informed about application start-up
	Public Shared Sub DoPrereq()
		Try
			' this function is called at application start-up in order to 
			' allow applications to do any prerequisites
			' there is no instance of this class at this time
			GetSett()
		Catch ex As System.Exception
			CMain.DebugException(ex)
		End Try
		Return
	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// create the user interface for this module
	Public Shared Sub DoCUI( _
	 ByRef pcs As CustomizationSection, _
	 ByRef mg As MacroGroup)
		' this function is called when a CUI is builded in order to
		' allow any application specifics
		Try
        #If HARDCORE_CUI
            If CApp.cuiCl Is Nothing then Exit Try
            CApp.cuiCl.AddMacro(sCMD_DIR,"PA_Dir")
            CApp.cuiCl.AddMacro(sCMD_ROT,"PA_Rot")
            CApp.cuiCl.AddMacro(sCMD_OFFS,"PA_Offset")
            CApp.cuiCl.AddMacro(sCMD_PT,"PA_Point")
            CApp.cuiCl.AddMacro(sCMD_OFFRNG,"PA_Range")
            CApp.cuiCl.AddMacro(sCMD_SETTINGS,"PA_Settings")

            CApp.cuiCl.AddToolbar( _
                sAPPNAME, _
                Application.MainWindow.Location.X, _
                100 + MIDS.POLYA * 30)
            CApp.cuiCl.AddToolbarBtn(CMain._T(tx.BYDIR),sCMD_DIR)
            CApp.cuiCl.AddToolbarBtn(CMain._T(tx.BYFIXPT),sCMD_ROT)
            CApp.cuiCl.AddToolbarBtn(CMain._T(tx.OFFSET),sCMD_OFFS)
            CApp.cuiCl.AddToolbarBtn(CMain._T(tx.MOVEPT),sCMD_PT)
            CApp.cuiCl.AddToolbarBtn(CMain._T(tx.OFFRNG),sCMD_OFFRNG)
            CApp.cuiCl.AddToolbarSep()
            CApp.cuiCl.AddToolbarBtn(CMain._T(tx.SETTINGS),sCMD_SETTINGS)

        #Else


			Dim mm1 As MenuMacro = Nothing
			mm1 = New MenuMacro(mg, sCMD_DIR, "^C^C" & sCMD_DIR, "ID_" & sCMD_DIR)
			mm1 = New MenuMacro(mg, sCMD_ROT, "^C^C" & sCMD_ROT, "ID_" & sCMD_ROT)
			mm1 = New MenuMacro(mg, sCMD_OFFS, "^C^C" & sCMD_OFFS, "ID_" & sCMD_OFFS)
			mm1 = New MenuMacro(mg, sCMD_PT, "^C^C" & sCMD_PT, "ID_" & sCMD_PT)
			mm1 = New MenuMacro(mg, sCMD_OFFRNG, "^C^C" & sCMD_OFFRNG, "ID_" & sCMD_OFFRNG)
			mm1 = New MenuMacro(mg, sCMD_SETTINGS, "^C^C" & sCMD_SETTINGS, "ID_" & sCMD_SETTINGS)

			Dim tbBtn As ToolbarButton = Nothing
			Dim newTb As Toolbar = New Toolbar(sAPPNAME, pcs.MenuGroup)
			newTb.ElementID = "EID_TNIK_PLYA"
			newTb.ToolbarOrient = ToolbarOrient.floating
			newTb.ToolbarVisible = ToolbarVisible.show
            newTb.XCoordinate = Application.MainWindow.Location.X
            newTb.YCoordinate = 100 + MIDS.POLYA * 30


			tbBtn = New ToolbarButton(newTb, -1)
			tbBtn.Name = CMain._T(tx.BYDIR)
			tbBtn.MacroID = "ID_" & sCMD_DIR

			tbBtn = New ToolbarButton(newTb, -1)
			tbBtn.Name = CMain._T(tx.BYFIXPT)
			tbBtn.MacroID = "ID_" & sCMD_ROT

			tbBtn = New ToolbarButton(newTb, -1)
			tbBtn.Name = CMain._T(tx.OFFSET)
			tbBtn.MacroID = "ID_" & sCMD_OFFS

			tbBtn = New ToolbarButton(newTb, -1)
			tbBtn.Name = CMain._T(tx.MOVEPT)
			tbBtn.MacroID = "ID_" & sCMD_PT

			tbBtn = New ToolbarButton(newTb, -1)
			tbBtn.Name = CMain._T(tx.OFFRNG)
			tbBtn.MacroID = "ID_" & sCMD_OFFRNG

			tbBtn = New ToolbarButton(newTb, -1)
			tbBtn.Name = CMain._T(tx.SETTINGS)
			tbBtn.MacroID = "ID_" & sCMD_SETTINGS

        #End If
		Catch ex As System.Exception
			CMain.DebugException(ex)
		End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
	End Sub
    '// ===================================================================== '


#End Region



#Region "Internal base work"


    '// --------------------------------------------------------------------- '
	'// For a point on a polyline, computes a oX relative angle that is inside
    '// Input: 
    '//     acPoly - the entity to check
    '//     iPtCnt - number of points to consider
    '//     kPt - 0 based index of the point to check
    '// Output: 
    '//     PtN1 - neighburing point that is lower
    '//     PtN2 - neighburing point that is higher
    '//     dAlfa1 - limiting angle that coresponds to PtN1
    '//     dAlfa2 - limiting angle that coresponds to PtN2
    '//     return: an angle that is between
    '//  For an observer standing in point kPt, the polyline is
    '// seen (counterclock wise) from PtN1 to PtN2
    Private Function GetInsideAngle( _
        ByRef pctPoly As PtMg, _
        ByVal kPt as Integer, _
        ByRef PtN1 As FSK_PT, _
        ByRef PtN2 As FSK_PT, _
        ByRef dAlfa1 As Double, _
        ByRef dAlfa2 As Double, _
        ByRef dAlfaInside As Double) As Boolean

        Dim dNSide As Double
        Dim dESide As Double
        Dim Ln As FSK_LINE
        Dim LnDiv As FSK_LINE
        Dim iTrP1 As Integer
        Dim iTrP2 As Integer
        Dim dEI As Double
        Dim dNI As Double
        Dim i As Integer
        Dim dDst As Double
        Dim pctIn As PtMg

        Dim rst As Integer = 0

        '// somehow I have the feeling that this monster may be reduced to a cute
        '// simple function; I am simply not smart enough to figure it out

        dNSide = pctPoly.pt(kPt).N_1
        dESide = pctPoly.pt(kPt).E_1
        
        PtN1.i = kPt - 1
        PtN2.i = kPt + 1
        If (PtN1.i < 0) Then
            PtN1.i = pctPoly.iUsd - 1
        End If
        If (PtN2.i >= pctPoly.iUsd) Then
            PtN2.i = 0
        End If

        PtN1.N_1 = pctPoly.pt(PtN1.i).N_1
        PtN1.E_1 = pctPoly.pt(PtN1.i).E_1
        PtN2.N_1 = pctPoly.pt(PtN2.i).N_1
        PtN2.E_1 = pctPoly.pt(PtN2.i).E_1
        dAlfa1 = Math.Atan2(PtN1.N_1 - dNSide, PtN1.E_1 - dESide)
        dAlfa2 = Math.Atan2(PtN2.N_1 - dNSide, PtN2.E_1 - dESide)
        While dAlfa1 < 0
            dAlfa1 = dAlfa1 + Math.PI*2
        End While
        While dAlfa2 < 0
            dAlfa2 = dAlfa2 + Math.PI*2
        End While
        dAlfaInside = (dAlfa1 + dAlfa2) / 2 

    ResetRestartLocation:

        LnDiv.E_1 = dESide
        LnDiv.N_1 = dNSide
        LnDiv.E_2 = dESide + Math.Cos(dAlfaInside)
        LnDiv.N_2 = dNSide + Math.Sin(dAlfaInside)
        If (Math.Abs(dAlfaInside - Math.PI/2) < CMain.MARGIN) Or _
           (Math.Abs(dAlfaInside - 3*Math.PI/2) < CMain.MARGIN) Then
            LnDiv.m = 0
            LnDiv.b = 0
            LnDiv.bVert =True
        Else
            LnDiv.m = Math.Tan(dAlfaInside)
            LnDiv.b = LnDiv.N_1 - LnDiv.m * LnDiv.E_1
            LnDiv.bVert = False
        End If

        pctIn.iAlc = pctPoly.iUsd \ 2
        pctIn.iUsd = 0
        ReDim pctIn.pt(0 to pctIn.iAlc-1)

        '// start with the line that defines Alfa1
        iTrP1 = 0
        Ln.N_1 = pctPoly.pt(iTrP1).N_1
        Ln.E_1 = pctPoly.pt(iTrP1).E_1

        iTrP2 = 1
        GoTo InsPoint

        Do

            '// only get points that are on dividing line

            If Ln.bVert = True Then
                If LnDiv.bVert = True Then
                    GoTo NextLineInPoly
                End If
                dEI = Ln.E_1
                dNI = LnDiv.b + LnDiv.m * dEI
            ElseIf LnDiv.bVert = True Then
                '// can't be paralel (rulled out)
                dEI = LnDiv.E_1
                dNI = Ln.b + Ln.m * dEI
            Else
                '// normal 2 lines intersection
                dDst = (Ln.m - LnDiv.m)
                If (Math.Abs(dDst) < CMain.MARGIN) Then
                    GoTo NextLineInPoly
                End If
                dEI = (LnDiv.b - Ln.b) / dDst
                dNI = LnDiv.b + LnDiv.m * dEI
            End If
            If (Math.Abs(Ln.E_2 - dEI) < CMain.MARGIN) Then
            If (Math.Abs(Ln.N_2 - dNI) < CMain.MARGIN) Then
                '// second point will be considered in next loop
                GoTo NextLineInPoly
            End If
            End If

            If ((Ln.E_1-CMain.MARGIN <= dEI) And (dEI < Ln.E_2+CMain.MARGIN)) Or _
               ((Ln.E_1+CMain.MARGIN >= dEI) And (dEI > Ln.E_2-CMain.MARGIN)) Then
                '// we have an intersection
                    CMain.AddPtToChain(pctIn,dNI,dEI)
                    GoTo NextLineInPoly
            End If

            NextLineInPoly:
            '// step to next line in the polyline
            Ln.N_1 = Ln.N_2
            Ln.E_1 = Ln.E_2
            iTrP1 = iTrP2
            iTrP2 = iTrP2 + 1
            If (iTrP1 = 0) Then Exit Do

            InsPoint:
            If (iTrP2 < 0) Then 
                iTrP2 = pctPoly.iUsd - 1
            ElseIf iTrP2 >= pctPoly.iUsd Then
                iTrP2 = 0
            End If
            Ln.N_2 = pctPoly.pt(iTrP2).N_1
            Ln.E_2 = pctPoly.pt(iTrP2).E_1
            dDst = (Ln.E_2 - Ln.E_1)
            If (Math.Abs(dDst) < CMain.MARGIN) Then
                Ln.bVert = True
                Ln.m = 0
                Ln.b = 0
            Else
                Ln.bVert = False
                Ln.m = ((Ln.N_2 - Ln.N_1) / dDst)
                Ln.b = Ln.N_1 - Ln.m * Ln.E_1
            End If
        Loop

        If (pctIn.iUsd < 2) Then
            ResetAngle:
            rst = rst + 1
            If (rst >= 10) Then
                Debug.Print("Give up in search for inside")
                Return False
            End If

            Debug.Print("Resetting the angle " & rst & " time")
            dAlfaInside = rst * (dAlfa1 + dAlfa2) / 10 
            GoTo ResetRestartLocation
        End If

        '// we now have a collection of points that has to be ordered
        If (LnDiv.bVert = True) Then
            For i = 0 to pctIn.iUsd - 2
                dNI = pctIn.pt(i).N_1
                iTrP2 = i
                For iTrP1 = i + 1 to pctIn.iUsd - 1
                    If (dNI > pctIn.pt(iTrP1).N_1) Then
                        iTrP2 = iTrP1
                        dNI = pctIn.pt(iTrP1).N_1
                    End If
                Next iTrP1
                If (iTrP2 <> i) Then
                    pctIn.pt(iTrP2).N_1 = pctIn.pt(i).N_1
                    pctIn.pt(i).N_1 = dNI
                End If
                pctIn.pt(i).i = pctPoly.iUsd
            Next i
            pctIn.pt(pctIn.iUsd-1).i = pctPoly.iUsd
        Else
            For i = 0 to pctIn.iUsd - 2
                dEI = pctIn.pt(i).E_1
                iTrP2 = i
                For iTrP1 = i + 1 to pctIn.iUsd - 1
                    If (dEI > pctIn.pt(iTrP1).E_1) Then
                        iTrP2 = iTrP1
                        dEI = pctIn.pt(iTrP1).E_1
                    End If
                Next iTrP1
                If (iTrP2 <> i) Then
                    dNI = pctIn.pt(iTrP2).N_1
                    pctIn.pt(iTrP2).E_1 = pctIn.pt(i).E_1
                    pctIn.pt(iTrP2).N_1 = pctIn.pt(i).N_1
                    pctIn.pt(i).E_1 = dEI
                    pctIn.pt(i).N_1 = dNI
                End If
                pctIn.pt(i).i = pctPoly.iUsd
            Next i
            pctIn.pt(pctIn.iUsd-1).i = pctPoly.iUsd
        End If
        '// that's that; a sorted collection

        '// find out if the points are vertex points or just intersections
        For i = 0 to pctPoly.iUsd - 1
            For iTrP1 = 0 to pctIn.iUsd - 1
                If (Math.Abs(pctPoly.pt(i).E_1 - _
                             pctIn.pt(iTrP1).E_1) < CMain.MARGIN) Then
                If (Math.Abs(pctPoly.pt(i).N_1 - _
                             pctIn.pt(iTrP1).N_1) < CMain.MARGIN) Then
                    pctIn.pt(iTrP1).i = i
                    Exit For
                End If
                End If
            Next iTrP1
        Next i

        '// if base point is first on a side or the other, we know
        '// the location of the polyline
        If (pctIn.pt(0).i = kPt) Then
            '// is at the second point
            i = 1
            GoTo  FoundTheAngle
        ElseIf (pctIn.pt(pctIn.iUsd-1).i = kPt) Then
            '// is at the first point
            i = 0
            GoTo  FoundTheAngle
        End If

        '// base point is somewhere in the middle
        '// loop-find it
        iTrP1 = 0 '// meaning outside
        iTrP2 = 0 '// meaning consistent state
        For i = 0 to pctIn.iUsd - 1
            If (pctIn.pt(i).i = pctPoly.iUsd) Then
                '// changing sides
                If (iTrP1 = 0) Then 
                    pctPoly.iUsd = 1
                Else
                    pctPoly.iUsd = 0
                End If
            ElseIf (pctIn.pt(i).i = kPt) Then
                '// found base point
                If (iTrP2 = 0) Then
                    If (pctPoly.iUsd = 0) Then
                        '// direction given by i+1
                        i = i + 1
                        GoTo  FoundTheAngle
                    Else
                        '// direction given by i-1
                        i = i - 1
                        GoTo  FoundTheAngle
                    End If
                End If
                '// inconsistent state
                '// we need to check the other end
                GoTo CheckEnd2
            Else
                iTrP2 = 1
                '// meaning inconsistent state
            End If
        Next
        '// if we reach the end of the loop the base point
        '// is not among the intersection results; which is
        '// kinda' weird, as I place it muself there
        '// place some number there just to find it
        Debug.Print("Weird or what?! Code 1212")
        Return False

    CheckEnd2:
        iTrP2 = i + 1 '// meaning consistent state
        iTrP1 = 0 '// meaning outside
        For i = pctIn.iUsd-1 to iTrP2 Step -1
            If (pctIn.pt(i).i = pctPoly.iUsd) Then
                '// changing sides
                If (iTrP1 = 0) Then 
                    pctPoly.iUsd = 1
                Else
                    pctPoly.iUsd = 0
                End If
            Else
                '//  getting inconsistent here, too
                '// we need to change that angle
                GoTo ResetAngle
            End If
        Next

        If (pctPoly.iUsd = 0) Then
            '// direction given by i-1
            i = i - 1
            GoTo  FoundTheAngle
        Else
            '// direction given by i+1
            i = i + 1
            GoTo  FoundTheAngle
        End If


    FoundTheAngle:
        '// i must have the index in pctIn of the point that,
        '// together with base point, define the inside angle

        dAlfaInside = Math.Atan2( _
            pctIn.pt(i).N_1 - dNSide, _
            pctIn.pt(i).E_1 - dESide)

        '// we need to make shure that, in counter-clock wise direction,
        '// first angle is first
        
        '// rotate the directions so that dAlfa1 is 0
        dEI = dAlfa2 - dAlfa1
        dNI = dAlfaInside - dAlfa1
        while (dEI < 0)
            dEI = Math.PI*2 + dEI
        End While
        while (dNI < 0)
            dNI = Math.PI*2 + dNI
        End While
        If (dNI > dEI) Then
            '// current order is not right and needs to be changed
            dNI = dAlfa1
            dAlfa1 = dAlfa2
            dAlfa2 = dNI
            Dim ptchg As FSK_PT
            ptchg = PtN1
            PtN1 = PtN2
            PtN2 = ptchg
        End If


        GC.Collect()
        GC.WaitForPendingFinalizers()
        Return True
    End Function
    '// ===================================================================== '


    '// --------------------------------------------------------------------- '
    '// Compute the area generated by a XLine dividing a polyline
    Private Function GetXlineArea( _
                        Byref ptPly as PtMg, _
                        Byref LnDiv As FSK_LINE, _
                        ByVal iStart As Integer, _
                        ByVal iAdv As Integer, _
                        ByRef pctIn As PtMg, _
                        ByRef pctOut As PtMg) _
             As Double


        Dim iTrP1 As Integer
        Dim iTrP2 As Integer
        Dim Ln As FSK_LINE
        Dim dDst As Double
        Dim dNI As Double
        Dim dEI As Double
        Dim dNPrev As Double
        Dim dEPrev As Double


        Const STS_IN As Integer = 0
        Const STS_OUT As Integer = 1
        '// we start inside since base point will always be part of resulting
        '// polyline
        Dim STS As Integer = STS_IN

        '// start with the line that defines Alfa1
        iTrP1 = iStart
        Ln.N_1 = ptPly.pt(iTrP1).N_1
        Ln.E_1 = ptPly.pt(iTrP1).E_1

        iTrP2 = iStart + iAdv
        If (iTrP2 < 0) Then 
            iTrP2 = ptPly.iUsd - 1
        ElseIf iTrP2 >= ptPly.iUsd Then
            iTrP2 = 0
        End If
        Ln.N_2 = ptPly.pt(iTrP2).N_1
        Ln.E_2 = ptPly.pt(iTrP2).E_1
        '// we already know that this line intersects dividing
        '// line in one point: the base point
        CMain.AddPtToChain(pctIn,Ln.N_1, Ln.E_1)
        CMain.AddPtToChain(pctOut,Ln.N_1, Ln.E_1)

        goto NextLineInPoly

        Do
            '// get the intersection point
            If Ln.bVert = True Then
                If LnDiv.bVert = True Then
                    '// paralel lines
                    If (STS = STS_OUT) Then
                        CMain.AddPtToChain(pctOut,Ln.N_1,Ln.E_1)
                    Else
                        CMain.AddPtToChain(pctIn,Ln.N_1,Ln.E_1)
                    End If
                    GoTo NextLineInPoly
                End If
                dEI = Ln.E_1
                dNI = LnDiv.b + LnDiv.m * dEI
            ElseIf LnDiv.bVert = True Then
                '// can't be paralel (rulled out)
                dEI = LnDiv.E_1
                dNI = Ln.b + Ln.m * dEI
            Else
                '// normal 2 lines intersection
                dDst = (Ln.m - LnDiv.m)
                If (Math.Abs(dDst) < CMain.MARGIN) Then
                    '// paralel lines
                    If (STS = STS_OUT) Then
                        CMain.AddPtToChain(pctOut,Ln.N_1,Ln.E_1)
                    Else
                        CMain.AddPtToChain(pctIn,Ln.N_1,Ln.E_1)
                    End If
                    GoTo NextLineInPoly
                End If
                dEI = (LnDiv.b - Ln.b) / dDst
                dNI = LnDiv.b + LnDiv.m * dEI
            End If
            '// see if this point is directly over first point
            If (Math.Abs(dEI - Ln.E_1) < CMain.MARGIN) Then
            If (Math.Abs(dNI - Ln.N_1) < CMain.MARGIN) Then
                '// this intersection is directly over first point
                    CMain.AddPtToChain(pctIn,dNI,dEI)
                    CMain.AddPtToChain(pctOut,dNI,dEI)

                    '// too see if the state is in or out, we need to analyse
                    '// the angles
                    Dim dA_Div As Double
                    Dim dA_back As Double
                    Dim dA_Fr As Double

                    If (LnDiv.bVert) Then
                        dA_Div = Math.PI / 2
                    Else
                        dA_Div = Math.Atan(LnDiv.m)
                    End If

                    dA_back = Math.Atan2( _
                        dNPrev - Ln.N_1, dEPrev - Ln.E_1)
                    CheckDir:
                    dA_Fr = Math.Atan2( _
                        Ln.N_2 - Ln.N_1, Ln.E_2 - Ln.E_1)
                    dA_back = dA_back - dA_Div
                    dA_Fr = dA_back - dA_Div
                    While (dA_back < 0) And (dA_Fr < 0)
                        dA_back = Math.PI*2 + dA_back
                        dA_Fr = Math.PI*2 + dA_Fr
                    End While
                    If (Math.Abs(dA_Fr) < CMain.MARGIN) Or _
                       (Math.Abs(dA_Fr - Math.PI) < CMain.MARGIN) Then
                        '// next point is still on dividing line

                        '// step to next line in the polyline
                        dNPrev = Ln.N_1
                        dEPrev = Ln.E_1
                        Ln.N_1 = Ln.N_2
                        Ln.E_1 = Ln.E_2
                        iTrP1 = iTrP2
                        iTrP2 = iTrP2 + iAdv

                        If (iTrP2 < 0) Then 
                            iTrP2 = ptPly.iUsd - 1
                        ElseIf iTrP2 >= ptPly.iUsd Then
                            iTrP2 = 0
                        End If
                        Ln.N_2 = ptPly.pt(iTrP1).N_1
                        Ln.E_2 = ptPly.pt(iTrP1).E_1

                        dDst = (Ln.E_2 - Ln.E_1)
                        If (Math.Abs(dDst) < CMain.MARGIN) Then
                            Ln.bVert = True
                            Ln.m = 0
                            Ln.b = 0
                        Else
                            Ln.bVert = False
                            Ln.m = ((Ln.N_2 - Ln.N_1) / dDst)
                            Ln.b = Ln.N_1 - Ln.m * Ln.E_1
                        End If
                        '// add both points
                        CMain.AddPtToChain(pctIn,dNI,dEI)
                        CMain.AddPtToChain(pctOut,dNI,dEI)
                        '// we already know these
                        dA_back = dA_Fr
                        GoTo CheckDir
                    End If
                    dA_back = dA_back * dA_Fr
                    If (dA_back < 0) Then
                        '// on diffrent sides
                        If (STS = STS_OUT) Then
                            STS = STS_IN
                        Else
                            STS = STS_OUT
                        End If
                    End If

                    GoTo NextLineInPoly

            End If
            End If
            '// see if this point is directly over second point
            If (Math.Abs(dEI - Ln.E_2) < CMain.MARGIN) Then
            If (Math.Abs(dNI - Ln.N_2) < CMain.MARGIN) Then
                '// this intersection is directly over second point
                '// and will be considered on next iteration
                    If (STS = STS_OUT) Then
                        CMain.AddPtToChain(pctOut,Ln.N_1,Ln.E_1)
                    Else
                        CMain.AddPtToChain(pctIn,Ln.N_1,Ln.E_1)
                    End If
                    GoTo NextLineInPoly
            End If
            End If

            If (Ln.bVert = True) Then
                If ((Ln.N_1 < dNI) And (dNI < Ln.N_2)) Or _
                   ((Ln.N_1 > dNI) And (dNI > Ln.N_2)) Then
                '// we have an intersection
                    If (STS = STS_OUT) Then
                        STS = STS_IN
                        CMain.AddPtToChain(pctOut,Ln.N_1,Ln.E_1)
                    Else
                        STS = STS_OUT
                        CMain.AddPtToChain(pctIn,Ln.N_1,Ln.E_1)
                    End If

                    CMain.AddPtToChain(pctIn,dNI,dEI)
                    CMain.AddPtToChain(pctOut,dNI,dEI)
                    GoTo NextLineInPoly
                Else
                    '// we're not changing the state
                    If (STS = STS_OUT) Then
                        CMain.AddPtToChain(pctOut,Ln.N_1,Ln.E_1)
                    Else
                        CMain.AddPtToChain(pctIn,Ln.N_1,Ln.E_1)
                    End If
                End If
            ElseIf ((Ln.E_1 < dEI) And (dEI < Ln.E_2)) Or _
               ((Ln.E_1 > dEI) And (dEI > Ln.E_2)) Then
                '// we have an intersection
                    If (STS = STS_OUT) Then
                        STS = STS_IN
                        CMain.AddPtToChain(pctOut,Ln.N_1,Ln.E_1)
                    Else
                        STS = STS_OUT
                        CMain.AddPtToChain(pctIn,Ln.N_1,Ln.E_1)
                    End If

                    CMain.AddPtToChain(pctIn,dNI,dEI)
                    CMain.AddPtToChain(pctOut,dNI,dEI)
                    GoTo NextLineInPoly
            Else
                '// we're not changing the state
                If (STS = STS_OUT) Then
                    CMain.AddPtToChain(pctOut,Ln.N_1,Ln.E_1)
                Else
                    CMain.AddPtToChain(pctIn,Ln.N_1,Ln.E_1)
                End If
            End If

            NextLineInPoly:
            '// step to next line in the polyline
            dNPrev = Ln.N_1
            dEPrev = Ln.E_1
            Ln.N_1 = Ln.N_2
            Ln.E_1 = Ln.E_2
            iTrP1 = iTrP2
            iTrP2 = iTrP2 + iAdv
            If (iTrP1 = iStart) Then Exit Do

            If (iTrP2 < 0) Then 
                iTrP2 = ptPly.iUsd - 1
            ElseIf iTrP2 >= ptPly.iUsd Then
                iTrP2 = 0
            End If
            Ln.N_2 = ptPly.pt(iTrP2).N_1
            Ln.E_2 = ptPly.pt(iTrP2).E_1
            dDst = (Ln.E_2 - Ln.E_1)
            If (Math.Abs(dDst) < CMain.MARGIN) Then
                Ln.bVert = True
                Ln.m = 0
                Ln.b = 0
            Else
                Ln.bVert = False
                Ln.m = ((Ln.N_2 - Ln.N_1) / dDst)
                Ln.b = Ln.N_1 - Ln.m * Ln.E_1
            End If
        Loop 

        GC.Collect()
        GC.WaitForPendingFinalizers()
        '// we now have two collections of points
        '// to compute the area for each of them is trivial
        return CalcAFromPt(pctIn)

    End Function
    '// ===================================================================== '


    '// --------------------------------------------------------------------- '
    Private Function GetTrgArea( ByRef adDoc as Document, _
                                 ByRef dMaxArea As Double, _
                                 ByRef bAskDir As Boolean) _
                                 as Boolean

        '// prepare array of values for single area mode
        ReDim lArea(0 to 0)
        lArea(0).sName = Nothing
        lArea(0).dArea = 0

        Dim pdr As Autodesk.AutoCAD.EditorInput.PromptDoubleResult
        Dim pdo As Autodesk.AutoCAD.EditorInput.PromptDoubleOptions
        
        pdo = New PromptDoubleOptions((vbLf & CMain._T(TX.TRGAREA) & ":"))
        pdo.AppendKeywordsToMessage = True
        pdo.AllowNegative = False
        pdo.AllowZero = False
        If (Stgs.dLastInA > 0) Then
            pdo.DefaultValue = Stgs.dLastInA
        End If
        Call pdo.Keywords.Add(CMain._T(TX.LIST))

        TryAgain:
        pdr = adDoc.Editor.GetDouble(pdo)
        If pdr.Status = PromptStatus.OK Then
            If (dMaxArea = 0) Then
                If (pdr.Value < Cmain.MARGIN) Then
                    CMain.AlertUser(String.Format( _
                                    Cmain._T(TX.ERR_PD_ASMAL), _
                                    pdr.Value))
                    GoTo TryAgain
                End If
            Else
                If (pdr.Value >= dMaxArea) Then
                    CMain.AlertUser(String.Format( _
                                    Cmain._T(TX.ERR_PD_AHP), _
                                    pdr.Value))
                    GoTo TryAgain
                End If
            End If
            lArea(0).dArea = pdr.Value
            Stgs.dLastInA = pdr.Value
        ElseIf pdr.Status = PromptStatus.Keyword Then
            If (pdr.StringResult = CMain._T(TX.LIST)) Then
                Dim dlgl As New CAa_Lst(dMaxArea)
                dlgl.ckAskDir.Visible = bAskDir
                If Application.ShowModalDialog(dlgl) _
                    <> Windows.Forms.DialogResult.OK Then
                    bAskDir = dlgl.ckAskDir.Checked
                    dlgl = Nothing
                    GoTo TryAgain
                End If
                dlgl = Nothing
            Else
                Return False
            End If
        Else
            Return False
        End If
        
        Return True
    End Function
    '// ===================================================================== '


    '// --------------------------------------------------------------------- '
	'// having a list of points, it computes the area
    Private Function CalcAFromPt(ByRef pct As PtMg) As Double
        
        '#If DEBUG
          '  Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
          '  Using acTrans As Transaction = _
          '      acDoc.Database.TransactionManager.StartTransaction()
          '  Try
          '      Dim acPoly As Polyline
          '      Dim acBlkTblRec As BlockTableRecord = _
          '          DirectCast(acTrans.GetObject( _
          '          acDoc.Database.CurrentSpaceId,OpenMode.ForWrite), _
          '          BlockTableRecord)
          '      acPoly = New Polyline(pct.iUsd)
          '      For j = 0 to pct.iUsd-1
          '          acPoly.AddVertexAt( j, _
          '              New Point2d(pct.pt(j).E_1, pct.pt(j).N_1),0,0,0)
          '      Next j
          '      acPoly.SetDatabaseDefaults(acDoc.Database)
          '      acPoly.Closed = True
	      '      acBlkTblRec.AppendEntity(acPoly)
		  '      acTrans.AddNewlyCreatedDBObject(acPoly, True)
          '      acTrans.Commit()
          '  Catch ex As System.Exception
          '      acTrans.Abort()
          '  End Try
          '  End Using
        '#End If


        Dim i As Integer
        Dim dSum  As Double = 0
        If (pct.iUsd < 3) Then
            Return 0
        Else
            For i = 1 to pct.iUsd-2
                dSum = dSum + _
                    (pct.pt(i).N_1 * (pct.pt(i-1).E_1 - pct.pt(i+1).E_1))
            Next
            dSum = dSum + _
                pct.pt(0).N_1 * (pct.pt(pct.iUsd-1).E_1 - pct.pt(1).E_1) + _
                pct.pt(pct.iUsd-1).N_1 * (pct.pt(pct.iUsd-2).E_1 - pct.pt(0).E_1)
            Return Math.Abs(dSum / 2)
        End If
    End Function
    '// ===================================================================== '


    '// --------------------------------------------------------------------- '
	'// only for horizontal variation; finds all intersection
    '// points and computes the area
    Private Function CalcDivArea(ByVal E As Double, _
                                 ByRef PlLines() as FSK_LINE, _
                                 ByRef pctIn As PtMg, _
                                 ByRef pctOut As PtMg) _
                                 As Double
        Const STS_IN As Integer = 0
        Const STS_OUT As Integer = 1
        '// we start inside since base point will always be part of resulting
        '// polyline
        Dim STS As Integer = STS_IN
        Dim dDif2 As Double

        Dim i As Integer
        Dim dNInt As Double
        Dim m As Double


        '// Iterate in all lines
        For i = 0 to UBound(PlLines)
            dDif2 = PlLines(i).E_2 - E
            If (Math.Abs(PlLines(i).E_1 - E) < CMain.MARGIN) Then
                '// P1 is directly over dividing line
                CMain.AddPtToChain(pctIn,PlLines(i).N_1, PlLines(i).E_1)
                CMain.AddPtToChain(pctOut,PlLines(i).N_1, PlLines(i).E_1)
                If (Math.Abs(dDif2) < CMain.MARGIN) Then
                    '// P2 is directly over dividing line, also
                    '// so there's no change in state
                Else If dDif2 < 0 Then
                    '// P2 is inside; chenge state to INSIDE
                    STS = STS_IN
                Else
                    '// P2 is outside; chenge state to OUTSIDE
                    STS = STS_OUT
                End If
                Continue For
            End If
            If (STS = STS_OUT) Then
                '// we were outside before (P1 is outside)
                CMain.AddPtToChain(pctOut,PlLines(i).N_1, PlLines(i).E_1)
                If (Math.Abs(dDif2) < CMain.MARGIN) Then
                    '// Second point is directly over dividing line
                    '// so there's no change in state
                Else If dDif2 > 0 Then
                    '// P2 is outside, so no change in state
                Else
                    '// P2 is inside; the line intersects dividing line;
                    '// add intersection point to both arrays
                    m = (PlLines(i).N_2 - PlLines(i).N_1) / _
                        (PlLines(i).E_2 - PlLines(i).E_1)
                    dNInt = m * (E - PlLines(i).E_1) + PlLines(i).N_1 
                    CMain.AddPtToChain(pctIn,dNInt, E)
                    CMain.AddPtToChain(pctOut,dNInt, E)
                    STS = STS_IN
                End If
            Else
                '// we were inside before (P1 is inside)
                CMain.AddPtToChain(pctIn,PlLines(i).N_1, PlLines(i).E_1)
                If (Math.Abs(dDif2) < CMain.MARGIN) Then
                    '// Second point is directly over dividing line
                    '// so there's no change in state

                Else If dDif2 < 0 Then
                    '// P2 is inside, so no change in state
                Else
                    '// P2 is outside; the line intersects dividing line;
                    '// add intersection point to both arrays
                    m = (PlLines(i).N_2 - PlLines(i).N_1) / _
                        (PlLines(i).E_2 - PlLines(i).E_1)
                    dNInt = m * (E - PlLines(i).E_1) + PlLines(i).N_1 
                    CMain.AddPtToChain(pctIn,dNInt, E)
                    CMain.AddPtToChain(pctOut,dNInt, E)
                    STS = STS_OUT
                End If
            End If
        Next i

        '// we now have two collections of points
        '// to compute the area for each of them is trivial
        return CalcAFromPt(pctIn)


    End Function
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// recreate a polyline from a list of points
    Private Function CreatePolyPoint(ByRef lArea As PAREA, _
                                ByRef pct as PtMg, _
                                ByVal dAlfaRad As double, _
                                ByVal dEBase As Double, _
                                ByVal dNBase As Double, _
                                ByVal dEOffset As Double, _
                                ByRef acDoc As Document, _
                                ByRef acTrans As Transaction, _
                                ByRef acBlkTblRec As BlockTableRecord) _
        As ObjectId

        If (pct.iUsd < 3) Then
            Return Nothing
        End If

        Dim PctCnt As Integer = pct.iUsd
        Dim acText As DBText

        If (Math.Abs(pct.pt(0).E_1 - pct.pt(pct.iUsd-1).E_1) < CMain.MARGIN) Then
        If (Math.Abs(pct.pt(0).N_1 - pct.pt(pct.iUsd-1).N_1) < CMain.MARGIN) Then
            PctCnt = PctCnt - 1
        End If
        End If

        Dim acPoly As New Polyline()
        Dim EPr As Double, NPr As Double

        acPoly.SetDatabaseDefaults(acDoc.Database)
        For i As Integer = 0 to PctCnt - 1

            pct.pt(i).E_1 = pct.pt(i).E_1 + dEOffset
            EPr = pct.pt(i).E_1 * Math.Cos(-dAlfaRad) - pct.pt(i).N_1 * Math.Sin(-dAlfaRad)
            NPr = pct.pt(i).E_1 * Math.Sin(-dAlfaRad) + pct.pt(i).N_1 * Math.Cos(-dAlfaRad)
            EPr = EPr + dEBase
            NPr = NPr + dNBase
            acPoly.AddVertexAt( _
                i, _
                New Point2d(EPr,NPr), _
                0,0,0)
            pct.pt(i).E_1 = pct.pt(i).E_1 - dEOffset
        Next
        acPoly.Closed = True
        
        Dim oIdPly As ObjectId = _
	    acBlkTblRec.AppendEntity(acPoly)
		acTrans.AddNewlyCreatedDBObject(acPoly, True)

        Dim EMed As Double = _
            (acPoly.GeometricExtents.MinPoint.X + _
             acPoly.GeometricExtents.MaxPoint.X) / 2
        Dim NMed As Double = _
            (acPoly.GeometricExtents.MinPoint.Y + _
             acPoly.GeometricExtents.MaxPoint.Y) / 2

        If Not (lArea.sName Is Nothing) Then
        If Not (lArea.sName = "") Then

            acText = New DBText()
            acText.SetDatabaseDefaults()
            acText.TextString = lArea.sName
            acText.Position = New Point3d(EMed, NMed, 0)
            acText.Height = 1.2
		    acBlkTblRec.AppendEntity(acText)
		    acTrans.AddNewlyCreatedDBObject(acText, True)
            
        End If
        End If

        acText = New DBText()
        acText.SetDatabaseDefaults()
        acText.TextString = String.Format( _
            CMain._T(TX.AREADISPLAY), _
            lArea.dArea.ToString("0"))
        acText.Position = New Point3d(EMed, NMed - 1.4, 0)
        acText.Height = 1.2
	    acBlkTblRec.AppendEntity(acText)
	    acTrans.AddNewlyCreatedDBObject(acText, True)

        GC.Collect()
        GC.WaitForPendingFinalizers()
        Return oIdPly
    End Function
    '// ===================================================================== '

#End Region


#Region "C O M M A N D S"


    '// --------------------------------------------------------------------- '
	'// display settings dialog
	<CommandMethod(sCMD_SETTINGS)> _
	Public Sub Show_Settings()
		Try
            Dim StgD As CST_PolyArea = New CST_PolyArea
            Application.ShowModelessDialog(StgD)
		Catch ex As System.Exception
            CMain.PopUpExc()
			CMain.DebugException(ex)
		End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// Divide a polyline by direction
	<CommandMethod(sCMD_DIR, CommandFlags.UsePickSet)> _
	Public Sub DivByDirection()

        '// Holds Original line selected by the user
        Dim Ln As FSK_LINE
        '// originally the angle of the slope, will become rotation angle
        Dim dAlfaRad As Double
        '// The area of the polyline to divide
        Dim dTotArea As Double
        '// Current area index to be generated
        Dim iA As Integer = 0


        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        If (acDoc Is Nothing) Then Return

	    Dim db As Database = acDoc.Database
	    Dim ed As Editor = acDoc.Editor
        Dim acPoly As Polyline = Nothing
        Dim acPlyCrt As Polyline = Nothing
        Dim idPlyCrt As ObjectId = Nothing
        Dim idOrig As ObjectId = Nothing
        Dim dNSide As Double
        Dim dESide As Double

        Dim acTrans As Transaction = _
            db.TransactionManager.StartTransaction()
        Try
            Dim acBlkTblRec As BlockTableRecord = DirectCast( _
                    acTrans.GetObject(db.CurrentSpaceId, _
                                        OpenMode.ForWrite), _
                    BlockTableRecord)

            '// =====================================================
            '// get a polyline to work with
            Do
		        If CMain.SelectPolyline(acTrans, acDoc, acPoly) = False Then Exit Try
                idOrig = acPoly.ObjectId
                If acPoly.NumberOfVertices >= 3 Then
                    If acPoly.Area < CMain.MARGIN * 3 Then
                        CMain.AlertUser(TX.ERR_AREASMALL)
                    Else
                        Exit Do
                    End If
                Else
                    CMain.AlertUser(TX.ERR_P3PT)
                End If
            Loop
            dTotArea = acPoly.Area
            '// =====================================================
            
            '// =====================================================
            '// Tell what is the desired area
            dim bAskD As Boolean = True
            If GetTrgArea(acDoc, dTotArea, bAskD) = False Then Exit Try
            iA = 0
            acPlyCrt = acPoly
            '// =====================================================

            LoopAskPoints:
            '// =====================================================
            '// Tell what is the desired direction
			Dim ppo As PromptPointOptions
			Dim ppr As PromptPointResult
            Dim bVert As Boolean = False
            Dim dDst As Double

			ppo = New PromptPointOptions(vbLf & CMain._T(TX.FPOINT) & ":")
            If (Stgs.bHavePrevDir) Then
                ppo.Keywords.Add(CMain._T(TX.PREVDIR))
            End If
			ppr = ed.GetPoint(ppo)
            ppo.Keywords.Clear()
			If ppr.Status = PromptStatus.OK Then
			    Ln.N_1 = ppr.Value.Y
			    Ln.E_1 = ppr.Value.X
            Else If ppr.Status = PromptStatus.Keyword Then
                Ln = Stgs.PrevDir
                GoTo HaveDir
            Else
                Exit Try
            End If

            ppo.BasePoint = ppr.Value
            ppo.UseBasePoint = True
            ppo.Message = vbLf & CMain._T(TX.SPOINT) & ":"
            Do
			    ppr = ed.GetPoint(ppo)
			    If ppr.Status <> PromptStatus.OK Then Exit Try
			    Ln.N_2 = ppr.Value.Y
			    Ln.E_2 = ppr.Value.X
                If Math.Abs(Ln.N_1 - Ln.N_2) > CMain.MARGIN Then Exit Do
                If Math.Abs(Ln.E_1 - Ln.E_2) > CMain.MARGIN Then Exit Do
                CMain.AlertUser(TX.ERR_PTCLOSE)
            Loop
            HaveDir:
            dDst = (Ln.E_2 - Ln.E_1)
            If (Math.Abs(dDst) < CMain.MARGIN) Then
                Ln.bVert = True
                Ln.m = 0
                Ln.b = 0
                dAlfaRad = Math.PI / 2
            Else
                Ln.bVert = False
                Ln.m = ((Ln.N_2 - Ln.N_1) / dDst)
                Ln.b = Ln.N_1 - Ln.m * Ln.E_1
                dAlfaRad = Math.Atan(Ln.m)
            End If
            Stgs.bHavePrevDir = True
            Stgs.PrevDir = Ln
            '// =====================================================
            
            '// =====================================================
            '// Pick a side
            ppo.UseBasePoint = False
            ppo.Message = vbLf & CMain._T(TX.PICKPONSIDE) & ":"
            Do
		        ppr = ed.GetPoint(ppo)
		        If ppr.Status <> PromptStatus.OK Then Exit Try
		        dNSide = ppr.Value.Y
		        dESide = ppr.Value.X
                If (CMain.IsPartOfLine(Ln,dNSide, dESide) = False) Then
                    Exit Do
                End If
                CMain.AlertUser(TX.ERR_PTONL)
            Loop
            '// =====================================================



            '// Create a paralel line in P3
            Dim lnP3Par As FSK_LINE
            CMain.DoParalel(Ln,dNSide,dESide,lnP3Par)

            '// Find closest point on the polyline to this line
            Dim k As Integer
            Dim acPt As Point2d
            Dim kDst As Integer = 0, lDst As Integer = 0
            Dim dDMin As Double, dDMax As Double
            acPt = acPlyCrt.GetPoint2dAt(0)
            Dim dTmpN As Double = acPt.Y
            Dim dTmpE As Double = acPt.X
            dDMin = CMain.DistToLine(lnP3Par,acPt.Y,acPt.X)
            dDMax = dDMin
            For k = 1 to acPlyCrt.NumberOfVertices - 1
                acPt = acPlyCrt.GetPoint2dAt(k)
                dDst = CMain.DistToLine(lnP3Par,acPt.Y,acPt.X)
                If (dDst < dDMin) Then
                    dDMin = dDst
                    kDst = k
                End If
                If (dDst > dDMax) Then
                    dDMax = dDst
                    lDst = k
                End If
            Next k
            '// acPt still has last point
            '// Number of sides (lines) that this polyline has)
            Dim iLnCnt = acPlyCrt.NumberOfVertices
            If Math.Abs(dTmpN - acPt.Y) < CMain.MARGIN Then
                If Math.Abs(dTmpE - acPt.X) < CMain.MARGIN Then
                    iLnCnt = iLnCnt - 1
                End If
            End If
            


            '// kDst - closest point to the polyline (0 based index)
            '// Create a paralel through here
            acPt = acPlyCrt.GetPoint2dAt(kDst)
            CMain.DoParalel(Ln,acPt.Y,acPt.X,lnP3Par)

            '// this line will be our Y axis
            '// Entire polyline is on the same side of this line
            Dim dEBase As Double = acPt.X
            Dim dNBase As Double = acPt.Y
            '// as we're iterating in requested polylines, this
            '// quantity increases to bring new virtual polylines
            '// at Y axis
            Dim dEOffset As Double = 0
            Dim EPr As Double, NPr As Double
            Dim i As Integer 


            '-----------------------------------------
            '// angle by which we rotate coordinate system
            '-----------------------------------------
            '// get some point on the polyline
            i = kDst + 1
            '// The loop is needed because one or more points may be
            '// on the same line as original point and the dividing
            '// direction may be paralel to thys line
            '// we are enshured that we're going to exit this loop
            '// by the fact that the area of the polyline is > 0
            Do
                If (i >= acPlyCrt.NumberOfVertices) Then i = 0
                '// move entire system on the origin
                acPt = acPlyCrt.GetPoint2dAt(i)
                EPr = acPt.X - dEBase
                NPr = acPt.Y - dNBase

                '// and rotate the system by the slope angle
                NPr = EPr * Math.Sin(-dAlfaRad) + NPr * Math.Cos(-dAlfaRad)
                If (Math.Abs(NPr) > Cmain.MARGIN) Then
                    Exit Do
                End If
                i = i + 1
            Loop 
            If (NPr < 0) Then
                '// If resulted Y is negative
                dAlfaRad = (Math.PI/2 - dAlfaRad)
            Else
                '// If resulted Y is positive
                dAlfaRad = -(Math.PI/2 + dAlfaRad)
            End If
            '-----------------------------------------


            '// This array will hold all lines that form the polyline
            i = 0 '// hold the index in list of lines
            Dim PlLines(iLnCnt-1) as FSK_LINE
            k = kDst '// shall hold the index of current point in polyline
            '// will start with base point wich is (0,0)
            PlLines(0).N_1 = 0
            PlLines(0).E_1 = 0
            Do
                k = k + 1
                If (k >= iLnCnt) Then k = 0
                acPt = acPlyCrt.GetPoint2dAt(k)

                EPr = acPt.X - dEBase
                NPr = acPt.Y - dNBase
                PlLines(i).E_2 = EPr * Math.Cos(dAlfaRad) - NPr * Math.Sin(dAlfaRad)
                PlLines(i).N_2 = EPr * Math.Sin(dAlfaRad) + NPr * Math.Cos(dAlfaRad)
                PlLines(i+1).E_1 = PlLines(i).E_2
                PlLines(i+1).N_1 = PlLines(i).N_2

                i = i + 1
            Loop Until i = iLnCnt-1
            '// last line is formed by last point and point kDst  wich is (0,0)
            PlLines(i).N_2 = 0
            PlLines(i).E_2 = 0
            Ins_Slopes:
            '// compute slope and y intercept; see if vertical
            For i = 0 to iLnCnt-1
                dTmpE = (PlLines(i).E_2 - PlLines(i).E_1)
                If (Math.Abs(dTmpE) < CMain.MARGIN) Then
                    PlLines(i).bVert = True
                    PlLines(i).m = 0
                    PlLines(i).b = 0
                Else
                    PlLines(i).m = (PlLines(i).N_2 - PlLines(i).N_1) / _
                                   dTmpE
                    PlLines(i).b = PlLines(i).N_1 - _
                                    PlLines(i).m * PlLines(i).E_1
                End If
            Next
            
            '// elements computed at N - 1
            Dim A1 As Double, X1 As Double
            '// elements computed at N - 2
            Dim A2 As Double, X2 As Double
            '// memory management for points
            Dim pctIn As PtMg
            Dim pctOut As PtMg

            pctIn.iAlc = iLnCnt
            pctIn.iUsd = 0
            Redim pctIn.pt(0 to pctIn.iAlc)
            pctOut.iAlc = iLnCnt
            pctOut.iUsd = 0
            Redim pctOut.pt(0 to pctOut.iAlc)

            X2 = dDMax - dDMin
            A2 = dTotArea
            X1 = X2 * lArea(iA).dArea / A2
            A1 = CalcDivArea(X1, PlLines, pctIn, pctOut)
            Dim An As Double, Xn As Double
            Dim ResetCnt As Integer = 0
            For i = 0 to stgs.iIterMax

                AreaLoopReset:

                '// somehow the numbers go into NaNs
                If Double.IsNaN(A2) Then
                    Debug.Print("DivByDirection: IsNaN(A2)")
                    GoTo ResetAreaLoop
                End If
                If Double.IsNaN(A1) Then
                    Debug.Print("DivByDirection: IsNaN(A1)")
                    GoTo ResetAreaLoop
                End If
                If (Math.Abs(A1 - lArea(iA).dArea)< CMain.MARGIN) Then
                    GoTo AreaFound
                End If
                pctIn.iAlc = iLnCnt
                pctIn.iUsd = 0
                Redim pctIn.pt(0 to pctIn.iAlc)
                pctOut.iAlc = iLnCnt
                pctOut.iUsd = 0
                Redim pctOut.pt(0 to pctOut.iAlc)

                Dim aDif As Double
                aDif = (A2 - A1)
                If (Math.Abs(aDif) < CMain.MARGIN) Then
                    Debug.Print("DivByDirection: (A2 - A1) = 0")
                    i = i + 1
                    GoTo ResetAreaLoop
                End If
                Xn = X1 - (A1 - lArea(iA).dArea)*(X2 - X1) / aDif
                An = CalcDivArea(Xn, PlLines, pctIn, pctOut)
                Debug.Print("X = " & Xn & ", A = " & An)
                If (An < CMain.MARGIN)  Or Double.IsNaN(An) Then
                    Debug.Print("DivByDirection: An - Nan or 0")
                    i = i + 1 
                    GoTo ResetAreaLoop
                End If
                A2 = A1
                X2 = X1
                A1 = An
                X1 = Xn
                Continue For

            ResetAreaLoop:
                Debug.Print("Reset in DivByDirection loop")
                ResetCnt = ResetCnt + 1
                X2 = (dDMax - dDMin) * ResetCnt / 110
                A2 = CalcDivArea(X2, PlLines, pctIn, pctOut)
                X1 = X2 * lArea(iA).dArea / A2
                A1 = CalcDivArea(X1, PlLines, pctIn, pctOut)
                If (ResetCnt >= 100) Then ResetCnt = 0

                pctIn.iAlc = iLnCnt
                pctIn.iUsd = 0
                Redim pctIn.pt(0 to pctIn.iAlc)
                pctOut.iAlc = iLnCnt
                pctOut.iUsd = 0
                Redim pctOut.pt(0 to pctOut.iAlc)

                GoTo AreaLoopReset

            Next i
            '// didn't find the area
            CMain.InformUser(String.Format( _
                        CMain._T(TX.ANOTFOUND), _
                        i))
            acTrans.Commit()
            Exit Try

        OKExitPoint:
            If Stgs.bDelInitPoly = True Then
                acPoly = _
                    DirectCast(acTrans.GetObject(idPlyCrt, _
                                                 OpenMode.ForWrite), _
                                                 Polyline)
                acPoly.Erase()
            End If
            acTrans.Commit()
            Exit Try

        AreaFound:
            Debug.Print("-- we have found the area --")
            '// we have found the area
            idPlyCrt = _
            CreatePolyPoint(lArea(iA), pctIn, dAlfaRad, dEBase, dNBase, _
                            dEOffset, acDoc, acTrans, acBlkTblRec)
            If (lArea(iA).sName = Nothing) Then
                CMain.InformUser(String.Format( _
                            CMain._T(TX.AFOUND), _
                            i+1,lArea(iA).dArea.ToString()))
            Else
                CMain.InformUser(String.Format( _
                            CMain._T(TX.AFOUND), _
                            i+1,lArea(iA).sName & "=" & _
                                lArea(iA).dArea.ToString()))
            End If
            '// See if there are any other areas to be generated
            If (iA < UBound(lArea)) Then
                iA = iA + 1
                '// Compute the area that was left
                dTotArea = CalcAFromPt(pctOut)
                If (dTotArea <= lArea(iA).dArea) Then
                    CreatePolyPoint(lArea(iA), pctOut, dAlfaRad, dEBase, dNBase, _
                                    dEOffset, acDoc, acTrans, acBlkTblRec)

                    '// this must be the last area
                    If (lArea(iA).sName = Nothing) Then
                        CMain.InformUser(String.Format( _
                                    CMain._T(TX.AFOUND), _
                                    1,lArea(iA).dArea.ToString()))
                    Else
                        CMain.InformUser(String.Format( _
                                    CMain._T(TX.AFOUND), _
                                    1,lArea(iA).sName & "=" & _
                                        lArea(iA).dArea.ToString()))
                    End If
                    GoTo OKExitPoint
                End If
                '// we need to create the array of lines from these points
                If (pctOut.iUsd < 3) Then
                    GoTo OKExitPoint
                End If
                '// Find out how manny points are left
                Dim PctCnt As Integer = pctOut.iUsd
                If (Math.Abs(pctOut.pt(0).E_1 - pctOut.pt(pctOut.iUsd-1).E_1) < CMain.MARGIN) Then
                If (Math.Abs(pctOut.pt(0).N_1 - pctOut.pt(pctOut.iUsd-1).N_1) < CMain.MARGIN) Then
                    PctCnt = PctCnt - 1
                End If
                End If
                ReDim PlLines(0 to PctCnt-1)
                iLnCnt = PctCnt
                '// we need to find new minimum X
                dDMin = pctOut.pt(0).E_1
                dDMax = dDMin
                kDst = 0
                lDst = 0
                Dim h As Integer
                For h = 1 to PctCnt - 1
                    If (pctOut.pt(h).E_1 < dDMin) Then
                        dDMin = pctOut.pt(h).E_1
                        kDst = h
                    End If
                    If (pctOut.pt(h).E_1 > dDMax) Then
                        dDMax = pctOut.pt(h).E_1
                        lDst = h
                    End If
                Next
                '// store this offset to be accounted for at reverse transformation
                dEOffset = dEOffset + dDMin
                '// new base point is kDst
                h = kDst
                dDMax = dDMax - dDMin
                
                i = 0
                PlLines(0).N_1 = pctOut.pt(0).N_1
                PlLines(0).E_1 = 0
                Do
                    h = h + 1
                    If (h >= pctOut.iUsd) Then h = 0
                    PlLines(i).E_2 = pctOut.pt(h).E_1 - dDMin
                    PlLines(i).N_2 = pctOut.pt(h).N_1
                    PlLines(i+1).E_1 = PlLines(i).E_2
                    PlLines(i+1).N_1 = PlLines(i).N_2
                    
                    i = i + 1
                Loop Until i = iLnCnt-1
                '// last line is formed by last point and point kDst which is (0,0)
                PlLines(i).N_2 = pctOut.pt(0).N_1
                PlLines(i).E_2 = 0

                '// do we need to ask for a new direction?
                If (bAskD) Then
                    'acTrans.Commit()
                    'acTrans.Dispose()
                    'acTrans  = _
                    '    db.TransactionManager.StartTransaction()
                    'acBlkTblRec = DirectCast( _
                    '    acTrans.GetObject(db.CurrentSpaceId, _
                    '                        OpenMode.ForWrite), _
                    '    BlockTableRecord)
                    'acPlyCrt = _
                    'DirectCast(acTrans.GetObject(idPlyCrt, _
                    '                             OpenMode.ForRead), _
                    '                             Polyline)
                    'GoTo LoopAskPoints
                End If

                GoTo Ins_Slopes
            Else
                '// we have exhausted the list
                '// if there is some left over and the user needs a polyline
                '// over that, do it now
                If Stgs.bLftOvr = True Then
                    '// Compute the area that was left
                    Dim pa As PAREA
                    pa.dArea = CalcAFromPt(pctOut)
                    pa.sName = Nothing
                    If (dTotArea > CMain.MARGIN) Then
                        CreatePolyPoint(pa, _
                                        pctOut, dAlfaRad, _
                                        dEBase, dNBase, _
                                        dEOffset, acDoc, _
                                        acTrans, acBlkTblRec)
                    End If
                End If
            End If

            GoTo OKExitPoint

        Catch ex As System.Exception
            acTrans.Abort()
            CMain.DebugException(ex)
            CMain.PopUpExc()
        Finally
            If (acTrans IsNot Nothing) Then
                acTrans.Dispose()
            End If
            GC.Collect()
            GC.WaitForPendingFinalizers()
        End Try
	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// Divide a polyline by a point (Find a direction)
	<CommandMethod(sCMD_ROT, CommandFlags.UsePickSet)> _
	Public Sub DivByRotation()

        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        If (acDoc Is Nothing) Then Return

	    Dim db As Database = acDoc.Database
	    Dim ed As Editor = acDoc.Editor
        Dim acPly As Polyline = Nothing

        Dim dNSide As Double
        Dim dESide As Double

		Dim ppo As PromptPointOptions
		Dim ppr As PromptPointResult

        Dim kPt As Integer
        Dim acPt As Point2d

        Dim LnDiv As FSK_LINE

        Dim pctPoly As PtMg
        Dim dTotArea As Double

        '// Current area index to be generated
        Dim iA As Integer = 0
        Dim i As Integer

        Dim iPtCnt As Integer


        Using acTrans As Transaction = _
            db.TransactionManager.StartTransaction()
        Try
            Dim acBlkTblRec As BlockTableRecord = DirectCast( _
                    acTrans.GetObject(db.CurrentSpaceId, _
                                        OpenMode.ForWrite), _
                    BlockTableRecord)

            '// =====================================================
            '// get a polyline to work with
            Do
		        If CMain.SelectPolyline(acTrans, acDoc, acPly) = False Then Exit Try
                acPt = acPly.GetPoint2dAt(0)
                dNSide = acPt.Y
                dESide = acPt.X
                acPt = acPly.GetPoint2dAt(acPly.NumberOfVertices-1)
                iPtCnt = acPly.NumberOfVertices
                If Math.Abs(dNSide - acPt.Y) < CMain.MARGIN Then
                If Math.Abs(dNSide - acPt.Y) < CMain.MARGIN Then
                    iPtCnt = iPtCnt - 1
                End If
                End If
                If iPtCnt >= 3 Then
                    If acPly.Area < CMain.MARGIN * 3 Then
                        CMain.AlertUser(TX.ERR_AREASMALL)
                    Else
                        Exit Do
                    End If
                Else
                    CMain.AlertUser(TX.ERR_P3PT)
                End If
            Loop
            pctPoly.iAlc = iPtCnt
            pctPoly.iUsd = iPtCnt
            Redim pctPoly.pt (0 to iPtCnt - 1)
            For i = 0 to iPtCnt - 1
                acPt = acPly.GetPoint2dAt(i)
                pctPoly.pt(i).E_1 = acPt.X
                pctPoly.pt(i).N_1 = acPt.Y
                pctPoly.pt(i).i = i
            Next
            dTotArea = acPly.Area
            '// =====================================================

            '// =====================================================
            '// Tell what is the desired area
            dim bAskD As Boolean = False
            If GetTrgArea(acDoc, dTotArea, bAskD) = False Then Exit Try
            iA = 0
            '// =====================================================

            '// =====================================================
            '// Pick a base point 

			ppo = New PromptPointOptions(vbLf & CMain._T(TX.FPOINT) & ":")
            ppo.UseBasePoint = False
            ppo.Message = vbLf & CMain._T(TX.PICKBASEP) & ":"
            Do
		        ppr = ed.GetPoint(ppo)
		        If ppr.Status <> PromptStatus.OK Then Exit Try
		        dNSide = ppr.Value.Y
		        dESide = ppr.Value.X
                kPt = 0
                For kPt = 0 to iPtCnt - 1
                    If Math.Abs(dNSide - pctPoly.pt(kPt).N_1) < CMain.MARGIN Then
                        If Math.Abs(dESide - pctPoly.pt(kPt).E_1) _
                            < CMain.MARGIN Then Exit Do
                    End If
                Next kPt
                CMain.AlertUser(TX.ERR_PTONL)
            Loop
            Debug.Print("Point " & kPt & _
                        "picked, N = " & dNSide & _
                        ", E = " & dESide)
            '// =====================================================


            '// =====================================================
            '// get it's neighburs
            Dim PtN1 As FSK_PT
            Dim PtN2 As FSK_PT
            Dim dAlfa1 As Double
            Dim dAlfa2 As Double
            Dim dAlfaInside As Double

            If GetInsideAngle( _
                pctPoly, kPt, PtN1, PtN2, _
                dAlfa1, dAlfa2, dAlfaInside) = False Then
                
                GoTo AreaNotFound
            End If
            '// =====================================================

            '// =====================================================
            '// The user needs to choose on which side the area stands
            ppo.UseBasePoint = False
            ppo.Message = vbLf & CMain._T(TX.PICKDIRINS) & ":"
            Dim dDN As Double, dDE As Double
            Do
                ppo.BasePoint = New Point3d(dESide, dNSide, 0)
                ppo.UseBasePoint = True
		        ppr = ed.GetPoint(ppo)
                dDE = ppr.Value.X - dESide
                dDN = ppr.Value.Y - dNSide
		        If ppr.Status <> PromptStatus.OK Then Exit Try
                If Math.Abs(dDN) < CMain.MARGIN Then
                If Math.Abs(dDE) < CMain.MARGIN Then
                    CMain.AlertUser(TX.ERR_PTCLOSE)
                    Continue Do
                End If
                End If
                Exit Do
            Loop
            Debug.Print("Point picked at N = " & ppr.Value.Y & _
                                        ", E = " & ppr.Value.X)
            '// =====================================================
            
            '// =====================================================
            Dim dAMed = (dAlfa2 + dAlfa1) / 2
            Dim dA__1 As Double = dAlfa1 - dAMed
            Dim dA__2 As Double = dAlfa2 - dAMed
            Dim dTrgA As Double = lArea(iA).dArea
            Dim bInvArea As Boolean = False
            Dim dAm As Double

            dAm = Math.Atan2(dDN, dDE)
            while (dAm < 0)
                dAm = Math.PI*2 + dAm
            End While

            dAm = dAm - dAMed
            While (dA__1 < 0) And (dA__2 < 0)
                dA__1 = dA__1 + Math.PI*2 + dA__1
                dA__2 = dA__1 + Math.PI*2 + dA__2
            End While    
            Dim tmpArea()  As PAREA 
            ReDim tmpArea(0 to UBound(lArea))
            If (dA__1 * dAm) < 0 Then
                dTrgA = dTotArea - dTrgA
                bInvArea = True
                For i = 0 to UBound(lArea)
                    tmpArea(UBound(lArea) - i) = lArea(i)
                Next
            Else
                For i = 0 to UBound(lArea)
                    tmpArea(i) = lArea(i)
                Next
            End If
            '// =====================================================
            
        ReEnterForNewArea:
            Dim A1 As Double, DX1 As Double
            Dim A2 As Double, DX2 As Double
            Dim iAdv As Integer = 1
            If (kPt = iPtCnt -1) Then
                If (PtN1.i <> 0) Then
                    iAdv = -1
                End If
            Else
                If (PtN1.i <> kPt+1) Then
                    iAdv = -1
                End If
            End If
            
            Dim pctIn As PtMg
            Dim pctOut As PtMg
            Dim An As Double
            Dim DXn As Double
            pctOut.iAlc = iPtCnt
            pctOut.iUsd = 0
            Redim pctOut.pt(0 to pctOut.iAlc)
            pctIn.iAlc = iPtCnt
            pctIn.iUsd = 0
            Redim pctIn.pt(0 to pctIn.iAlc)
            
            '// state that for entire angle coresponds entire area
            A2 = dTotArea
            If (dAlfa2 > dAlfa1) Then
                DX2 = dAlfa2 - dAlfa1
            Else
                DX2 = (Math.PI*2 - dAlfa1) + dAlfa2
            End If
            
            '// estimate the angle using previous statement
            DX1 = DX2 * dTrgA / dTotArea
            dAlfaInside = DX1 + dAlfa1
            
            If (dAlfaInside < 0)
                dAlfaInside = Math.PI*2 + dAlfaInside
            ElseIf (dAlfaInside > Math.PI*2)
                dAlfaInside = dAlfaInside -  Math.PI*2
            End If
            
            LnDiv.E_1 = dESide
            LnDiv.N_1 = dNSide
            LnDiv.E_2 = dESide + Math.Cos(dAlfaInside)
            LnDiv.N_2 = dNSide + Math.Sin(dAlfaInside)
            If (Math.Abs(dAlfaInside - Math.PI/2) < CMain.MARGIN) Or _
               (Math.Abs(dAlfaInside - 3*Math.PI/2) < CMain.MARGIN) Then
                LnDiv.m = 0
                LnDiv.b = 0
                LnDiv.bVert =True
            Else
                LnDiv.m = Math.Tan(dAlfaInside)
                LnDiv.b = LnDiv.N_1 - LnDiv.m * LnDiv.E_1
                LnDiv.bVert = False
            End If
            A1 = GetXlineArea(pctPoly, LnDiv, kPt, iAdv, pctIn, pctOut)
            pctOut.iAlc = iPtCnt
            pctOut.iUsd = 0
            Redim pctOut.pt(0 to pctOut.iAlc)
            pctIn.iAlc = iPtCnt
            pctIn.iUsd = 0
            Redim pctIn.pt(0 to pctIn.iAlc)

            '// =====================================================
            '// Main loop used to search for area
            For i = 0 to Stgs.iIterMax
                
                DXn = DX1 - (A1 - dTrgA) * (DX2 - DX1) / (A2 - A1)
                dAlfaInside = DXn + dAlfa1

                If (dAlfaInside < 0)
                    dAlfaInside = Math.PI*2 + dAlfaInside
                ElseIf (dAlfaInside > Math.PI*2)
                    dAlfaInside = dAlfaInside -  Math.PI*2
                End If
                
                LnDiv.E_1 = dESide
                LnDiv.N_1 = dNSide
                LnDiv.E_2 = dESide + Math.Cos(dAlfaInside)
                LnDiv.N_2 = dNSide + Math.Sin(dAlfaInside)
                If (Math.Abs(dAlfaInside - Math.PI/2) < CMain.MARGIN) Or _
                   (Math.Abs(dAlfaInside - 3*Math.PI/2) < CMain.MARGIN) Then
                    LnDiv.m = 0
                    LnDiv.b = 0
                    LnDiv.bVert =True
                Else
                    LnDiv.m = Math.Tan(dAlfaInside)
                    LnDiv.b = LnDiv.N_1 - LnDiv.m * LnDiv.E_1
                    LnDiv.bVert = False
                End If
                An = GetXlineArea(pctPoly, LnDiv, kPt, iAdv, pctIn, pctOut)
                Debug.Print("X = " & DXn & ", A = " & An)
                If (Math.Abs(A1 - dTrgA) < CMain.MARGIN) Then
                    '// There ya go
                    GoTo AreaFound
                End If


                A2 = A1
                DX2 = DX1
                A1 = An
                DX1 = DXn

                pctOut.iAlc = iPtCnt
                pctOut.iUsd = 0
                Redim pctOut.pt(0 to pctOut.iAlc)
                pctIn.iAlc = iPtCnt
                pctIn.iUsd = 0
                Redim pctIn.pt(0 to pctIn.iAlc)
            Next i
            '// =====================================================
AreaNotFound:
            '// didn't find the area
            CMain.InformUser(String.Format( _
                        CMain._T(TX.ANOTFOUND), _
                        i))
            acTrans.Commit()
            Exit Try

        OKExitPoint:
            If Stgs.bDelInitPoly = True Then
                acPly.UpgradeOpen()
                acPly.Erase()
            End If
            acTrans.Commit()
            Exit Try


        AreaFound:
            Debug.Print("-- we have found the area --")

            If bInvArea = True Then
                CreatePolyPoint(tmpArea(iA), pctOut, 0, 0, 0, _
                                0, acDoc, acTrans, acBlkTblRec)
                dTotArea = CalcAFromPt(pctIn)
            Else
                CreatePolyPoint(tmpArea(iA), pctIn, 0, 0, 0, _
                                0, acDoc, acTrans, acBlkTblRec)
                dTotArea = CalcAFromPt(pctOut)
            End If
            '// we have found the area
            If (tmpArea(iA).sName = Nothing) Then
                CMain.InformUser(String.Format( _
                            CMain._T(TX.AFOUND), _
                            i+1,dTrgA.ToString()) & vbNewLine)
            Else
                CMain.InformUser(String.Format( _
                            CMain._T(TX.AFOUND), _
                            i+1,tmpArea(iA).sName & "=" & _
                                dTrgA.ToString()) & vbNewLine)
            End If

            kPt = 0
            iAdv = 1
            If (iA < UBound(tmpArea)) Then
                iA = iA + 1
                If bInvArea = True Then
                    dAlfa2 = dAlfaInside
                Else
                    dAlfa1 = dAlfaInside
                End If
                '// Compute the area that was left
                If (dTotArea <= tmpArea(iA).dArea) Then
                    If bInvArea = True Then
                        CreatePolyPoint(tmpArea(iA), pctIn, 0, 0, 0, _
                                        0, acDoc, acTrans, acBlkTblRec)
                    Else
                        CreatePolyPoint(tmpArea(iA), pctOut, 0, 0, 0, _
                                        0, acDoc, acTrans, acBlkTblRec)
                    End If
                    '// this must be the last area
                    If (tmpArea(iA).sName = Nothing) Then
                        CMain.InformUser(String.Format( _
                                    CMain._T(TX.AFOUND), _
                                    1,tmpArea(iA).dArea.ToString()))
                    Else
                        CMain.InformUser(String.Format( _
                                    CMain._T(TX.AFOUND), _
                                    1,tmpArea(iA).sName & "=" & _
                                        tmpArea(iA).dArea.ToString()))
                    End If
                    GoTo OKExitPoint
                Else
                    If bInvArea = True Then
                        dTrgA = dTotArea - tmpArea(iA).dArea
                        pctPoly = pctIn
                    Else
                        dTrgA = tmpArea(iA).dArea
                        pctPoly = pctOut
                    End If
                    GoTo ReEnterForNewArea
                End If
            Else
                '// we have exhausted the list
                '// if there is some left over and the user needs a polyline
                '// over that, do it now
                If Stgs.bLftOvr = True Then
                    '// Compute the area that was left
                    Dim pa As PAREA
                    If bInvArea = False Then
                        pa.dArea = CalcAFromPt(pctOut)
                        pa.sName = Nothing
                        If (dTotArea > CMain.MARGIN) Then
                            CreatePolyPoint(pa, _
                                            pctOut, 0, _
                                            0, 0, _
                                            0, acDoc, _
                                            acTrans, acBlkTblRec)
                        End If
                    Else
                        pa.dArea = CalcAFromPt(pctIn)
                        pa.sName = Nothing
                        If (dTotArea > CMain.MARGIN) Then
                            CreatePolyPoint(pa, _
                                            pctIn, 0, _
                                            0, 0, _
                                            0, acDoc, _
                                            acTrans, acBlkTblRec)
                        End If
                    End If
                End If
            End If
            

            If Stgs.bDelInitPoly = True Then
                acPly.UpgradeOpen()
                acPly.Erase()
            End If
            acTrans.Commit()
        Catch ex As System.Exception
            CMain.PopUpExc()
            acTrans.Abort()
            CMain.DebugException(ex)
        End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
        End Using

	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// Create a new polyline by offseting existing one
	<CommandMethod(sCMD_OFFS, CommandFlags.UsePickSet)> _
	Public Sub OffsetToArea()
        Dim acDbObjColl As DBObjectCollection
        Dim i As Integer
        Dim iA As Integer = 0

        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        If (acDoc Is Nothing) Then Return

	    Dim db As Database = acDoc.Database
	    Dim ed As Editor = acDoc.Editor
        Dim acPoly As Polyline = Nothing
        Dim acTmpP1 As Polyline = Nothing
        Dim acTmpP2 As Polyline = Nothing

        Dim p3Dc As New Point3dCollection 

        Using acTrans As Transaction = _
            db.TransactionManager.StartTransaction()
        Try
            Dim acBlkTblRec As BlockTableRecord = DirectCast( _
                    acTrans.GetObject(db.CurrentSpaceId, _
                                        OpenMode.ForWrite), _
                    BlockTableRecord)
            
		    If CMain.SelectPolyline(acTrans, acDoc, acPoly, False) _
		        = False Then Exit Try
            dim bAskD As Boolean = False
            If GetTrgArea(acDoc, 0, bAskD) = False Then Exit Try


            Dim X1 As Double = 0
            '// holds current polyline


            acTmpP1 = DirectCast(acPoly.Clone(), Polyline)
            acTmpP1.Closed = True
            '// holds offset polyline
            Dim X2 As Double = 1

            For i = 0 to stgs.iIterMax

                '// Offset the polyline a given distance
                acDbObjColl = acPoly.GetOffsetCurves(X2)

                If (acDbObjColl Is Nothing) Then
                    Debug.Print("(acDbObjColl Is Nothing)")
                    CMain.AlertUser(String.Format( _
                                CMain._T(TX.ERR_FUNKY), _
                                X2))
                    GoTo FailToGenerate
                End If
                If (acDbObjColl.Count <> 1) Then
                    Debug.Print("(acDbObjColl.Count <> 1)")
                    CMain.AlertUser(String.Format( _
                                CMain._T(TX.ERR_FUNKY), _
                                X2))
                    GoTo FailToGenerateFree
                End If
                If Not (TypeOf acDbObjColl.Item(0) Is Polyline) Then
                    Debug.Print("(TypeOf acDbObjColl.Item(0) <> Polyline)")
                    CMain.AlertUser(String.Format( _
                                CMain._T(TX.ERR_FUNKY), _
                                X2))
                    GoTo FailToGenerateFree
                End If
                acTmpP2 = DirectCast(acDbObjColl.Item(0), Polyline)
                Debug.Print("X = " & X2 & ", A = " & acTmpP2.Area)
                acTmpP2.IntersectWith( _
                        acTmpP2, _
                        Intersect.OnBothOperands, _
                        p3Dc,0,0)
                If (p3Dc.Count <> acTmpP2.NumberOfVertices) Then
                    CMain.AlertUser(String.Format( _
                                CMain._T(TX.ERR_PLYINTI), _
                                X2))
                    acTmpP1.Dispose()
                    acTmpP2.Dispose()
                    acTmpP1 = Nothing
                    acTmpP2 = Nothing

                    acTrans.Commit()
                    Exit Try
                End If

                If Math.Abs(acTmpP2.Area - lArea(iA).dArea) < CMain.MARGIN Then
                    Debug.Print("-- we have found the area --")
                    '// we have found the solution
                    If (lArea(iA).sName = Nothing) Then
                        CMain.InformUser(String.Format( _
                                    CMain._T(TX.AFOUND), _
                                    i+1,lArea(iA).dArea.ToString()))
                    Else
                        CMain.InformUser(String.Format( _
                                    CMain._T(TX.AFOUND), _
                                    i+1,lArea(iA).sName & "=" & _
                                        lArea(iA).dArea.ToString()))
                    End If
                    acTmpP1.Dispose()
                    acTmpP1 = Nothing
	                acBlkTblRec.AppendEntity(acTmpP2)
		            acTrans.AddNewlyCreatedDBObject(acTmpP2, True)
                    acTrans.Commit()
                    acTmpP2 = Nothing
                    Exit Try
                End If

                Dim Xn As Double
                Xn = X2 + (X2 - X1)/(acTmpP2.Area - acTmpP1.Area) * _
                        (lArea(iA).dArea - acTmpP2.Area)
                
                X1 = X2
                acTmpP1.Dispose()
                acTmpP1 = acTmpP2
                acTmpP2 = Nothing
                X2 = Xn
                p3Dc.Clear()
                acDbObjColl.Clear()
            Next i
            acTmpP1.Dispose()
            acTmpP2.Dispose()
            acTmpP1 = Nothing
            acTmpP2 = Nothing

            '// didn't find the area
            CMain.AlertUser(String.Format( _
                        CMain._T(TX.ANOTFOUND), _
                        i))

            acTrans.Commit()
            Exit Try

    FailToGenerateFree:
            '' Step through the new objects created
            For Each acEnt As Entity In acDbObjColl
                acEnt.Dispose()
            Next
    FailToGenerate:
            Debug.Print("-- FailToGenerate --")
            acTrans.Abort()
            If Not (acTmpP1  Is Nothing) Then
                acTmpP1.Dispose()
                acTmpP1 = Nothing
            End If


        Catch ex As System.Exception
            CMain.PopUpExc()
            acTrans.Abort()
            CMain.DebugException(ex)
        Finally
            If (p3Dc Is Nothing) Then
                p3Dc.Dispose()
                p3Dc = Nothing
            End If
        End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
        End Using
	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// Get to an area by moving a point on a direction
	<CommandMethod(sCMD_PT, CommandFlags.UsePickSet)> _
	Public Sub MovePtToArea()
        
        Dim i As Integer
        Dim iA As Integer = 0

        '// Holds Original line selected by the user
        Dim Ln As FSK_LINE
        '// the angle of the slope
        Dim dAlfaRad As Double

        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        If (acDoc Is Nothing) Then Return

	    Dim db As Database = acDoc.Database
	    Dim ed As Editor = acDoc.Editor
        Dim acPoly As Polyline = Nothing

        Dim dNSide As Double
        Dim dESide As Double

        Using acTrans As Transaction = _
            db.TransactionManager.StartTransaction()
        Try
            Dim acBlkTblRec As BlockTableRecord = DirectCast( _
                    acTrans.GetObject(db.CurrentSpaceId, _
                                        OpenMode.ForWrite), _
                    BlockTableRecord)
            '// =====================================================
            '// get a polyline to work with
            Do
		        If CMain.SelectPolyline(acTrans, acDoc, acPoly) = False Then Exit Try
                If acPoly.NumberOfVertices >= 3 Then
                    If acPoly.Area < CMain.MARGIN * 3 Then
                        CMain.AlertUser(TX.ERR_AREASMALL)
                    Else
                        Exit Do
                    End If
                Else
                    CMain.AlertUser(TX.ERR_P3PT)
                End If
            Loop
            '// =====================================================
            
            '// =====================================================
            '// Tell what is the desired area
            dim bAskD As Boolean = False
            If GetTrgArea(acDoc, 0, bAskD) = False Then Exit Try
            '// =====================================================
            
            '// =====================================================
            '// Tell what is the desired direction
			Dim ppo As PromptPointOptions
			Dim ppr As PromptPointResult

			ppo = New PromptPointOptions(vbLf & CMain._T(TX.FPOINT) & ":")
            If (Stgs.bHavePrevDir) Then
                ppo.Keywords.Add(CMain._T(TX.PREVDIR))
            End If
            
			ppr = ed.GetPoint(ppo)
		    If ppr.Status = PromptStatus.OK Then
		        Ln.N_1 = ppr.Value.Y
		        Ln.E_1 = ppr.Value.X
            Else If ppr.Status = PromptStatus.Keyword Then
                Ln = Stgs.PrevDir
                GoTo HaveDir
            Else
                Exit Try
            End If

            ppo.BasePoint = ppr.Value
            ppo.UseBasePoint = True
            ppo.Message = vbLf & CMain._T(TX.SPOINT) & ":"
            Dim dDn As Double = Ln.N_1 - Ln.N_2
            Dim dDe As Double = Ln.E_1 - Ln.E_2
            Do
			    ppr = ed.GetPoint(ppo)
                    
			    Ln.N_2 = ppr.Value.Y
			    Ln.E_2 = ppr.Value.X
                HaveDir:
                dDn = Ln.N_1 - Ln.N_2
                dDe = Ln.E_1 - Ln.E_2
                If Math.Abs(dDn) > CMain.MARGIN Then
                    Ln.m = dDn / dDe
                    Ln.bVert = False
                    Ln.b = Ln.N_1 - Ln.m*Ln.E_1
                    dAlfaRad = Math.Atan2(dDn, dDe)
                    Exit Do
                End If
                If Math.Abs(dDe) > CMain.MARGIN Then
                    Ln.bVert = True
                    Ln.m = 0
                    Ln.b = 0
                    If (dDe > 0) Then
                        dAlfaRad = Math.PI / 2
                    Else
                        dAlfaRad = 3 * Math.PI / 2
                    End If
                    Exit Do
                End If
                CMain.AlertUser(TX.ERR_PTCLOSE)
            Loop
            Stgs.bHavePrevDir = True
            Stgs.PrevDir = Ln
            '// =====================================================

            '// =====================================================
            '// Pick a point to move on polyline
            Dim kPt As Integer
            Dim acPt As Point2d
            ppo.UseBasePoint = False
            ppo.Message = vbLf & CMain._T(TX.PICKPTOMOVE) & ":"
            Do
		        ppr = ed.GetPoint(ppo)
		        If ppr.Status <> PromptStatus.OK Then Exit Try
		        dNSide = ppr.Value.Y
		        dESide = ppr.Value.X
                kPt = 0
                For kPt = 0 to acPoly.NumberOfVertices - 1
                    acPt = acPoly.GetPoint2dAt(kPt)
                    If Math.Abs(dNSide - acPt.Y) < CMain.MARGIN Then
                        If Math.Abs(dESide - acPt.X) < CMain.MARGIN Then Exit Do
                    End If
                Next kPt
                CMain.AlertUser(TX.ERR_PTONL)
            Loop
            '// =====================================================
            Debug.Print("Point " & kPt & _
                        "picked, N = " & dNSide & _
                        ", E = " & dESide)

            acPoly.UpgradeOpen()

            '// elements computed at N - 1
            Dim A1 As Double, D1 As Double
            '// elements computed at N - 2
            Dim A2 As Double, D2 As Double
            '// current elements
            Dim An As Double, Dn As Double

            With acPoly.GeometricExtents
                Dn = Math.Sqrt( _
                     Math.Pow(.MaxPoint.X - .MinPoint.X, 2) + _
                     Math.Pow(.MaxPoint.Y - .MinPoint.Y, 2)) 
            End With

            D2 = Dn / 1000
            If (Ln.bVert) Then
                dNSide = dNSide+D2
            Else
                dNSide = dNSide+D2*Math.Sin(dAlfaRad)
                dESide = dESide+D2*Math.Cos(dAlfaRad)
            End If
            acPoly.SetPointAt(kPt, New Point2d(dESide, dNSide))
            A2 = acPoly.Area
            
            D1 = 2 * Dn / 1000
            If (Ln.bVert) Then
                dNSide = dNSide+D1
            Else
                dNSide = dNSide+D1*Math.Sin(dAlfaRad)
                dESide = dESide+D1*Math.Cos(dAlfaRad)
            End If
            acPoly.SetPointAt(kPt, New Point2d(dESide, dNSide))
            A1 = acPoly.Area
            
            Dim ResetCnt As Integer = 0
            For i = 0 to Stgs.iIterMax

                AreaLoopReset:

                '// somehow the numbers go into NaNs
                If Double.IsNaN(A2) Then
                    Debug.Print("MovePtToArea: IsNaN(A2)")
                    GoTo ResetAreaLoop
                End If
                If Double.IsNaN(A1) Then
                    Debug.Print("MovePtToArea: IsNaN(A1)")
                    GoTo ResetAreaLoop
                End If
                If (Math.Abs(A1 - lArea(iA).dArea)< CMain.MARGIN) Then
                    GoTo AreaFound
                End If
                

                Dim aDif As Double
                aDif = (A2 - A1)
                If (Math.Abs(aDif) < CMain.MARGIN) Then
                    Debug.Print("MovePtToArea: (A2 - A1) = 0")
                    i = i + 1
                    GoTo ResetAreaLoop
                End If
                Dn = D1 - (A1 - lArea(iA).dArea)*(D2 - D1) / aDif

                acPoly.SetPointAt(kPt, New Point2d( _
                                  dESide+Dn*Math.Cos(dAlfaRad), _
                                  dNSide+Dn*Math.Sin(dAlfaRad)))
                An = acPoly.Area
                
                Debug.Print("X = " & Dn & _
                            ", A = " & An & _
                            ", N = " &  dNSide+Dn*Math.Sin(dAlfaRad) & _
                            ", E = " &  dESide+Dn*Math.Cos(dAlfaRad))
                If (An < CMain.MARGIN) Or Double.IsNaN(An) Then
                    Debug.Print("MovePtToArea: An - Nan or 0")
                    i = i + 1
                    GoTo ResetAreaLoop
                End If
                A2 = A1
                D2 = D1
                A1 = An
                D1 = Dn
                Continue For

            ResetAreaLoop:
                Debug.Print("Reset in MovePtToArea loop")
                ResetCnt = ResetCnt + 1

                'GoTo AreaLoopReset
                GoTo OKExitPoint

            Next i
            acPoly.SetPointAt(kPt, New Point2d( _
                              dESide, _
                              dNSide))
            '// didn't find the area
            CMain.InformUser(String.Format( _
                        CMain._T(TX.ANOTFOUND), _
                        i))

        OKExitPoint:
            acTrans.Commit()
            Exit Try

        AreaFound:
            Debug.Print("-- we have found the area --")
            '// we have found the area
            If (lArea(iA).sName = Nothing) Then
                CMain.InformUser(String.Format( _
                            CMain._T(TX.AFOUND), _
                            i+1,lArea(iA).dArea.ToString()))
            Else
                CMain.InformUser(String.Format( _
                            CMain._T(TX.AFOUND), _
                            i+1,lArea(iA).sName & "=" & _
                                lArea(iA).dArea.ToString()))
            End If


            acTrans.Commit()
        Catch ex As System.Exception
            CMain.PopUpExc()
            acTrans.Abort()
            CMain.DebugException(ex)
        End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
        End Using
	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// Similar to offset method, but only a range of points are moved
	<CommandMethod(sCMD_INSPT, CommandFlags.UsePickSet)> _
	Public Sub InsPointInPoly()

        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        If (acDoc Is Nothing) Then Return

	    Dim db As Database = acDoc.Database
	    Dim ed As Editor = acDoc.Editor
        Dim acPoly As Polyline = Nothing
		Dim ppo As PromptPointOptions
		Dim ppr As PromptPointResult
        Dim pt_1 As FSK_PT

        Using acTrans As Transaction = _
            db.TransactionManager.StartTransaction()
        Try
            
		    If CMain.SelectPolyline(acTrans, acDoc, acPoly) = False Then Exit Try
            '// =====================================================
            '// Pick first point 
            With    pt_1
			    ppo = New PromptPointOptions(vbLf & CMain._T(TX.FPOINT) & ":")
                ppo.UseBasePoint = False
                Do
		            ppr = ed.GetPoint(ppo)
		            If ppr.Status <> PromptStatus.OK Then Exit Try
		            .N_1 = ppr.Value.Y
		            .E_1 = ppr.Value.X
                    For .i = 0 to acPoly.NumberOfVertices - 1
                        Dim pt2 As Point2d
                        pt2 = acPoly.GetPoint2dAt(.i)
                        If Math.Abs(ppr.Value.Y - pt2.Y) < CMain.MARGIN Then
                            If Math.Abs(ppr.Value.X - pt2.X) _
                                < CMain.MARGIN Then Exit Do
                        End If
                    Next .i
                    CMain.AlertUser(TX.ERR_PTONL)
                Loop
                Debug.Print("Point1 " & .i & _
                            " picked, N = " & .N_1 & _
                            ", E = " & .E_1)
            End With
            '// =====================================================


            acTrans.Commit()
        Catch ex As System.Exception
            acTrans.Abort()
            CMain.DebugException(ex)
            CMain.PopUpExc()
        End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
        End Using
	End Sub
    '// ===================================================================== '

    '// --------------------------------------------------------------------- '
	'// Similar to offset method, but only a range of points are moved
	<CommandMethod(sCMD_OFFRNG, CommandFlags.UsePickSet)> _
	Public Sub MoveRangeToArea()

        Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
        If (acDoc Is Nothing) Then Return

	    Dim db As Database = acDoc.Database
	    Dim ed As Editor = acDoc.Editor
        Dim acPoly As Polyline = Nothing
        Dim dArea As Double
		Dim ppo As PromptPointOptions
		Dim ppr As PromptPointResult
        Dim acTmpP1 As Polyline = Nothing
        Dim acTmpP2 As Polyline = Nothing
        Dim acDbObjColl As DBObjectCollection
        Dim p3Dc As New Point3dCollection 
        Dim iA As Integer

        Using acTrans As Transaction = _
            db.TransactionManager.StartTransaction()
        Try
            
		    If CMain.SelectPolyline(acTrans, acDoc, acPoly) = False Then Exit Try
            dim bAskD As Boolean = False
            If GetTrgArea(acDoc, dArea, bAskD) = False Then Exit Try

            Dim pt_1 As FSK_PT
            Dim pt_2 As FSK_PT
            Dim pt_3 As FSK_PT

            Dim acBlkTblRec As BlockTableRecord = DirectCast( _
                    acTrans.GetObject(db.CurrentSpaceId, _
                                        OpenMode.ForWrite), _
                    BlockTableRecord)

            '// =====================================================
            '// Pick first point 
            With    pt_1
			    ppo = New PromptPointOptions(vbLf & CMain._T(TX.FPOINT) & ":")
                ppo.UseBasePoint = False
                Do
		            ppr = ed.GetPoint(ppo)
		            If ppr.Status <> PromptStatus.OK Then Exit Try
		            .N_1 = ppr.Value.Y
		            .E_1 = ppr.Value.X
                    For .i = 0 to acPoly.NumberOfVertices - 1
                        Dim pt2 As Point2d
                        pt2 = acPoly.GetPoint2dAt(.i)
                        If Math.Abs(ppr.Value.Y - pt2.Y) < CMain.MARGIN Then
                            If Math.Abs(ppr.Value.X - pt2.X) _
                                < CMain.MARGIN Then Exit Do
                        End If
                    Next .i
                    CMain.AlertUser(TX.ERR_PTONL)
                Loop
                End With
            '// =====================================================

            '// =====================================================
            '// Pick second point 
            With    pt_2
			    ppo = New PromptPointOptions(vbLf & CMain._T(TX.SPOINT) & ":")
                ppo.UseBasePoint = False
                Do
		            ppr = ed.GetPoint(ppo)
		            If ppr.Status <> PromptStatus.OK Then
                        '// only point 1 is to be used
                        pt_2 = pt_1
                        GoTo Points_Were_Picked
                    End if
		            .N_1 = ppr.Value.Y
		            .E_1 = ppr.Value.X
                    For .i = 0 to acPoly.NumberOfVertices - 1
                        Dim pt2 As Point2d
                        pt2 = acPoly.GetPoint2dAt(.i)
                        If Math.Abs(ppr.Value.Y - pt2.Y) < CMain.MARGIN Then
                            If Math.Abs(ppr.Value.X - pt2.X) _
                                < CMain.MARGIN Then
                                '// it must not be the same as 
                                '// first point
                                If (.i = pt_1.i) Then
                                    CMain.AlertUser(TX.ERRP_ID1)
                                    Continue Do
                                End If
                                Exit Do
                            End If
                        End If
                    Next .i
                    CMain.AlertUser(TX.ERR_PTONL)
                Loop
                End With
            '// =====================================================

            '// =====================================================
            '// Pick third point 
            With    pt_3
			    ppo = New PromptPointOptions(vbLf & CMain._T(TX.TPOINT) & ":")
                ppo.UseBasePoint = False
                Do
		            ppr = ed.GetPoint(ppo)
		            If ppr.Status <> PromptStatus.OK Then
                        '// only point 1 and 2 are to be used
                        '// this is only valid if 1 and 2 are consecutive
                        If (pt_1.i = pt_2.i + 1) Then
                            pt_3 = pt_2
                            pt_2 = pt_1
                            pt_1 = pt_3
                            GoTo Points_Were_Picked
                        Else If (pt_1.i = pt_2.i - 1) Then
                            GoTo Points_Were_Picked
                        ElseIf (pt_1.i = 0) AndAlso (pt_2.i = acPoly.NumberOfVertices - 1) Then
                            pt_3 = pt_2
                            pt_2 = pt_1
                            pt_1 = pt_3
                            GoTo Points_Were_Picked
                        ElseIf (pt_2.i = 0) AndAlso (pt_1.i = acPoly.NumberOfVertices - 1) Then
                            GoTo Points_Were_Picked
                        End If
                    End if
		            .N_1 = ppr.Value.Y
		            .E_1 = ppr.Value.X
                    For .i = 0 to acPoly.NumberOfVertices - 1
                        Dim pt2 As Point2d
                        pt2 = acPoly.GetPoint2dAt(.i)
                        If Math.Abs(ppr.Value.Y - pt2.Y) < CMain.MARGIN Then
                            If Math.Abs(ppr.Value.X - pt2.X) _
                                < CMain.MARGIN Then
                                '// it must not be the same as 
                                '// first point
                                If (.i = pt_1.i) Then
                                    CMain.AlertUser(TX.ERRP_ID1)
                                    Continue Do
                                ElseIf (.i = pt_2.i) Then
                                    CMain.AlertUser(TX.ERRP_ID2)
                                    Continue Do
                                End If
                                Exit Do
                            End If
                        End If
                    Next .i
                    CMain.AlertUser(TX.ERR_PTONL)
                Loop
                Debug.Print("Point3 " & .i & _
                            " picked, N = " & .N_1 & _
                            ", E = " & .E_1)
            End With
            '// =====================================================
            Dim tI1 As Integer, tI2 As Integer, tI3 as Integer
            tI1 = pt_1.i
            tI2 = pt_2.i
            tI3 = pt_3.i
            If tI2 < tI1 Then tI2 = tI2 + acPoly.NumberOfVertices
            If tI3 < tI1 Then tI3 = tI3 + acPoly.NumberOfVertices
            If (tI1 < tI2) AndAlso (tI2 > tI3) Then
                '// current order is the correct one
                '// pt_2 = pt_3
            Else
                pt_2 = pt_1
                pt_1 = pt_3
            End If
Points_Were_Picked:

            '// at this point only 2 points are used (1 and 2)
            '// the section that will be modified is from 1, in ascending
            '// order, till 2
            '// 1 and 2 are moved
            '// 1 may be equal with 2 (only a point is moved)
            
            


            acTmpP1 = DirectCast(acPoly.Clone(), Polyline)
            acTmpP1.Closed = True
            '// holds offset polyline
            Dim X2 As Double = 1
            Dim X1 As Double
            Dim APrev As Double = acTmpP1.Area
            For i = 0 to stgs.iIterMax

                '// Offset the polyline a given distance
                acDbObjColl = acPoly.GetOffsetCurves(X2)

                If (acDbObjColl Is Nothing) Then
                    Debug.Print("(acDbObjColl Is Nothing)")
                    CMain.AlertUser(String.Format( _
                                CMain._T(TX.ERR_FUNKY), _
                                X2))
                    GoTo FailToGenerate
                End If
                If (acDbObjColl.Count <> 1) Then
                    Debug.Print("(acDbObjColl.Count <> 1)")
                    CMain.AlertUser(String.Format( _
                                CMain._T(TX.ERR_FUNKY), _
                                X2))
                    GoTo FailToGenerateFree
                End If
                If Not (TypeOf acDbObjColl.Item(0) Is Polyline) Then
                    Debug.Print("(TypeOf acDbObjColl.Item(0) <> Polyline)")
                    CMain.AlertUser(String.Format( _
                                CMain._T(TX.ERR_FUNKY), _
                                X2))
                    GoTo FailToGenerateFree
                End If
                acTmpP2 = DirectCast(acDbObjColl.Item(0), Polyline)
                acTmpP2.IntersectWith( _
                        acTmpP2, _
                        Intersect.OnBothOperands, _
                        p3Dc,0,0)
                If (p3Dc.Count <> acTmpP2.NumberOfVertices) Then
                    CMain.AlertUser(String.Format( _
                                CMain._T(TX.ERR_PLYINTI), _
                                X2))
                    acTmpP1.Dispose()
                    acTmpP2.Dispose()
                    acTmpP1 = Nothing
                    acTmpP2 = Nothing
                    
                    acTrans.Commit()
                    Exit Try
                End If
                Dim k As Integer
                k = pt_1.i
                Do
                    acTmpP1.SetPointAt(k, acTmpP2.GetPoint2dAt(k))
                    If k = pt_2.i Then Exit Do
                    k = k + 1
                    If k >= acPoly.NumberOfVertices Then k = 0
                Loop


                Debug.Print("X = " & X2 & ", A = " & acTmpP1.Area)
                If Math.Abs(acTmpP1.Area - lArea(iA).dArea) < CMain.MARGIN Then
                    Debug.Print("-- we have found the area --")
                    '// we have found the solution
                    If (lArea(iA).sName = Nothing) Then
                        CMain.InformUser(String.Format( _
                                    CMain._T(TX.AFOUND), _
                                    i+1,lArea(iA).dArea.ToString()))
                    Else
                        CMain.InformUser(String.Format( _
                                    CMain._T(TX.AFOUND), _
                                    i+1,lArea(iA).sName & "=" & _
                                        lArea(iA).dArea.ToString()))
                    End If
                    acTmpP2.Dispose()
                    acTmpP2 = Nothing
	                acBlkTblRec.AppendEntity(acTmpP1)
		            acTrans.AddNewlyCreatedDBObject(acTmpP1, True)
                    acTrans.Commit()
                    acTmpP1 = Nothing

                    If Stgs.bDelInitPoly = True Then
                        acPoly.UpgradeOpen()
                        acPoly.Erase()
                    End If
                    Exit Try

                    Exit Try
                End If

                Dim Xn As Double
                Xn = X2 + (X2 - X1)/(acTmpP1.Area - APrev) * _
                        (lArea(iA).dArea - acTmpP1.Area)
                
                X1 = X2
                APrev = acTmpP1.Area
                acTmpP2.Dispose()
                acTmpP2 = Nothing
                X2 = Xn
                p3Dc.Clear()
                acDbObjColl.Clear()
            Next i
            acTmpP1.Dispose()
            acTmpP2.Dispose()
            acTmpP1 = Nothing
            acTmpP2 = Nothing

            CMain.AlertUser(TX.ERR_CMDI)
            Exit Try

        FailToGenerateFree:
            '' Step through the new objects created
            For Each acEnt As Entity In acDbObjColl
                acEnt.Dispose()
            Next
        FailToGenerate:
            acTrans.Commit()
        Catch ex As System.Exception
            acTrans.Abort()
            CMain.DebugException(ex)
            CMain.PopUpExc()
        End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
        End Using
	End Sub
    '// ===================================================================== '

#End Region






End Class
'//  CLASS    =============================================================== '
'//
'//
'//
'//
'//  FUNCTIONS    ----------------------------------------------------------- '

'//  FUNCTIONS    =========================================================== '
'//
'//
'//
'//
'// ------------------------------------------------------------------------- '
'// ========================================================================= '


